如何开始

导入数据

打开 Power BI Desktop

导入5张Excel表

点击顶部菜单 主页 → 获取数据 → Excel工作簿

在弹窗中找到你下载的 HR人力资源数据.xlsx,选中后点击 打开

选择数据表:你会看到一个导航器窗口,左边列出了5个Sheet:

  • ✅ 员工信息表
  • ✅ 考勤表
  • ✅ 绩效表
  • ✅ 薪酬表
  • ✅ 离职表

全部勾选这5个,然后点击右下角的 转换数据

进入Power Query编辑器后,逐一点击每个表,检查列名上方的图标:

  • 日期列(如入职日期、日期、离职日期)必须显示 📅 日历图标
  • 数字列(如年龄、绩效得分、薪酬总额)必须显示 1️⃣ 2️⃣ 3️⃣ 数字图标
  • 文本列(如姓名、部门、离职原因)显示 ABC 图标

如果发现数字列显示ABC(文本),右键该列 → 更改类型 → 整数/小数

检查完成后,点击左上角 关闭并应用(会加载一会儿)

导入时的关键设置(每张表都要做):

  • 在导航器窗口勾选表格 → 点击 转换数据
  • 检查数据类型是否正确(日期列必须是日期类型,数字列不能是文本)
  • 点击左上角 关闭并应用

建立数据模型关系

这是最重要的一步!点击左侧 模型视图(三个方块图标)

你需要建立这些关系:

1
2
3
4
员工信息表[员工ID]  ←──1:N──→  考勤表[员工ID]
员工信息表[员工ID] ←──1:N──→ 绩效表[员工ID]
员工信息表[员工ID] ←──1:N──→ 薪酬表[员工ID]
员工信息表[员工ID] ←──1:N──→ 离职表[员工ID]

操作方法:

  1. 在模型视图中,把 员工信息表员工ID 字段 拖拽考勤表员工ID
  2. 松开鼠标,自动创建关系
  3. 双击关系线,确认:
    • 基数:一对多 (1:N)
    • 交叉筛选方向:单一(从员工信息表指向其他表)

创建日期表

点击 建模 → 新建表,粘贴这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
日期表 = 
ADDCOLUMNS(
CALENDAR(DATE(2023,1,1), DATE(2024,12,31)),
"年", FORMAT(YEAR([Date]), "0"),
"月", MONTH([Date]),
"月名称", FORMAT([Date], "MMM"),
"季度", "Q" & QUARTER([Date]),
"年季度", YEAR([Date]) & "Q" & QUARTER([Date]),
"年月", FORMAT([Date], "YYYY-MM"),
"星期", WEEKDAY([Date], 2),
"是否工作日", IF(WEEKDAY([Date], 2) <= 5, "工作日", "周末")
)

然后建立关系:

  • 日期表[Date] ←──1:N──→ 考勤表[日期]
  • 日期表[Date] ←──1:N──→ 离职表[离职日期]

创建度量值

点击 建模 → 新建度量值,逐个创建以下核心指标:

  1. 人员数量(最基础的指标)
在职人数 = DISTINCTCOUNT('员工信息表'[员工ID])
  1. 离职人数
离职人数 = DISTINCTCOUNT('离职表'[员工ID])
  1. 离职率(核心KPI)
离职率 = DIVIDE([离职人数], [在职人数] + [离职人数], 0)

选中这个度量值 → 在工具栏设置格式为 百分比

  1. 平均绩效得分
平均绩效得分 = AVERAGE('绩效表'[绩效得分])
  1. 薪酬总额
薪酬总额 = SUM('薪酬表'[薪酬总额])
  1. 人均薪酬
人均薪酬 = DIVIDE([薪酬总额], DISTINCTCOUNT('薪酬表'[员工ID]), 0)
  1. 平均司龄(月)
1
2
3
4
5
平均司龄_月 = 
AVERAGEX(
'员工信息表',
DATEDIFF('员工信息表'[入职日期], TODAY(), MONTH)
)

制作第一个页面

然后就可以开始制作可视化大屏了~~

DAX

基础语法规则

度量值 vs 计算列

类型 创建方式 用途 写法
度量值 建模 → 新建度量值 聚合计算(如总和、平均值) = 公式
计算列 建模 → 新建列 每行新增一个字段 = 公式

核心函数

聚合函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 求和
sum('薪酬表'[薪酬总额])

// 平均值
average('绩效表'[绩效得分])

// 计数(非空值)
count('员工信息表'[员工id])

// 去重计数(最常用)
distinctcount('员工信息表'[员工id])

// 最大值
max('绩效表'[绩效得分])

// 最小值
min('绩效表'[绩效得分])

安全除法(避免除以0报错)

1
2
3
4
5
// 基础写法:divide(分子, 分母, 除0时的返回值)
divide([离职人数], [在职人数], 0)

// 等价于:iferror(分子/分母, 0)
// 但divide性能更好

逻辑判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// if函数
if('员工信息表'[在职状态] = "在职", 1, 0)

// switch多条件判断(比嵌套if更清晰)
switch(
true(),
'绩效表'[绩效等级] = "a", "优秀",
'绩效表'[绩效等级] = "b", "良好",
'绩效表'[绩效等级] = "c", "合格",
"待改进"
)

// and/or
and('员工信息表'[年龄] > 30, '员工信息表'[职级] = "经理")
or('员工信息表'[部门] = "销售部", '员工信息表'[部门] = "市场部")

筛选函数

calculate(改变筛选上下文)

1
2
3
4
5
6
7
8
9
10
11
12
// 基础:只计算销售部的离职人数
calculate(
distinctcount('离职表'[员工id]),
'员工信息表'[部门] = "销售部"
)

// 多条件:销售部且主动离职
calculate(
distinctcount('离职表'[员工id]),
'员工信息表'[部门] = "销售部",
'离职表'[离职类型] = "主动离职"
)

filter(返回筛选后的表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 筛选出绩效a级的员工
filter(
'员工信息表',
related('绩效表'[绩效等级]) = "a"
)

// 结合calculate使用
calculate(
[在职人数],
filter(
'员工信息表',
datediff('员工信息表'[入职日期], today(), year) >= 3
)
)

all / allexcept / allselected(清除筛选)

1
2
3
4
5
6
7
8
// all:忽略所有筛选,返回全表
calculate([在职人数], all('员工信息表'))

// allselected:只忽略用户手动筛选,保留视觉交叉筛选
calculate([在职人数], allselected('员工信息表'))

// allexcept:只保留指定列的筛选,清除其他
calculate([在职人数], allexcept('员工信息表', '员工信息表'[部门]))

时间智能函数

基础时间函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 今天
today()

// 当前日期时间
now()

// 日期差(月)
datediff('员工信息表'[入职日期], today(), month)

// 日期加减
dateadd('日期表'[日期], -1, month) // 上月
dateadd('日期表'[日期], -1, year) // 去年
dateadd('日期表'[日期], -1, quarter) // 上季度

// 月初/月末
startofmonth('日期表'[日期])
endofmonth('日期表'[日期])

累计计算

1
2
3
4
5
6
7
8
// ytd(年初至今)
totalytd([薪酬总额], '日期表'[日期])

// qtd(季初至今)
totalqtd([薪酬总额], '日期表'[日期])

// mtd(月初至今)
totalmtd([薪酬总额], '日期表'[日期])

同期对比

1
2
3
4
5
6
7
8
9
10
11
12
// 去年同期
sameperiodlastyear('日期表'[日期])

// 上月
parallelperiod('日期表'[日期], -1, month)

// 结合calculate使用
去年同期离职率 =
calculate(
[离职率],
sameperiodlastyear('日期表'[日期])
)

表函数(进阶)

创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 筛选表
calculatetable('员工信息表', '员工信息表'[部门] = "销售部")

// 添加列
addcolumns(
'员工信息表',
"司龄月", datediff('员工信息表'[入职日期], today(), month)
)

// 汇总表
summarize(
'员工信息表',
'员工信息表'[部门],
"人数", distinctcount('员工信息表'[员工id])
)

迭代函数(逐行计算)

1
2
3
4
5
6
7
8
// sumx:对表的每一行计算后求和
sumx('薪酬表', '薪酬表'[基本工资] * 1.2)

// averagex:对表的每一行计算后求平均
averagex('员工信息表', datediff('员工信息表'[入职日期], today(), year))

// minx/maxx
minx('绩效表', '绩效表'[绩效得分])

变量(var / return)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 用变量让公式更清晰
离职率 =
var 离职人数 = distinctcount('离职表'[员工id])
var 在职人数 = distinctcount('员工信息表'[员工id])
var 总人数 = 离职人数 + 在职人数
return
divide(离职人数, 总人数, 0)

// 多变量
绩效奖金 =
var 基本工资 = '薪酬表'[基本工资]
var 绩效系数 = related('绩效表'[目标完成率])
var 部门系数 =
switch(
'员工信息表'[部门],
"销售部", 1.3,
"技术部", 1.2,
1.0
)
return
基本工资 * 绩效系数 * 部门系数

关系函数

1
2
3
4
5
6
7
8
9
10
11
// related(从多端查一端,需要建立关系)
related('员工信息表'[部门])

// relatedtable(从一端查多端,返回表)
relatedtable('考勤表')

// lookupvalue(跨表查找,不需要关系)
lookupvalue(
'员工信息表'[部门],
'员工信息表'[员工id], '离职表'[员工id]
)

常用面试度量值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// 1. 在职人数
在职人数 = distinctcount('员工信息表'[员工id])

// 2. 离职人数
离职人数 = distinctcount('离职表'[员工id])

// 3. 离职率
离职率 = divide([离职人数], [在职人数] + [离职人数], 0)

// 4. 平均绩效得分
平均绩效得分 = average('绩效表'[绩效得分])

// 5. 薪酬总额
薪酬总额 = sum('薪酬表'[薪酬总额])

// 6. 人均薪酬
人均薪酬 = divide([薪酬总额], distinctcount('薪酬表'[员工id]), 0)

// 7. 平均司龄(月)
平均司龄_月 = averagex('员工信息表', datediff('员工信息表'[入职日期], today(), month))

// 8. 主动离职率
主动离职率 =
var 主动离职 = calculate([离职人数], '离职表'[离职类型] = "主动离职")
return
divide(主动离职, [在职人数] + [离职人数], 0)

// 9. 新员工离职率(入职6个月内)
新员工离职率 =
var 新员工离职 =
calculate(
[离职人数],
datediff(related('员工信息表'[入职日期]), '离职表'[离职日期], month) <= 6
)
return
divide(新员工离职, [在职人数] + [离职人数], 0)

// 10. 离职率同比变化
离职率同比变化 =
var 当前 = [离职率]
var 去年 = calculate([离职率], sameperiodlastyear('日期表'[日期]))
return
当前 - 去年

// 11. 高绩效离职率
高绩效离职率 =
var 高绩效离职 =
calculate(
[离职人数],
related('绩效表'[绩效等级]) in {"a", "b"}
)
return
divide(高绩效离职, [在职人数] + [离职人数], 0)

// 12. 关键人才保留率
关键人才保留率 =
var 关键人才 =
filter(
'员工信息表',
datediff('员工信息表'[入职日期], today(), year) >= 3
)
var 关键人才总数 = countrows(关键人才)
var 保留人数 =
countrows(
filter(关键人才, '员工信息表'[在职状态] = "在职")
)
return
divide(保留人数, 关键人才总数, 0)

// 13. 考勤异常率
考勤异常率 =
var 异常天数 =
calculate(
countrows('考勤表'),
'考勤表'[出勤状态] <> "正常"
)
var 总天数 = countrows('考勤表')
return
divide(异常天数, 总天数, 0)

// 14. 离职风险评分
离职风险评分 =
var 考勤风险 = min([考勤异常率] * 500, 100)
var 绩效风险 =
var 当前 = [平均绩效得分]
var 上期 = calculate([平均绩效得分], dateadd('日期表'[日期], -1, quarter))
return min(max(上期 - 当前, 0) * 5, 100)
var 薪酬风险 =
var 竞争力 = divide([人均薪酬], percentilex.inc(allselected('员工信息表'), [人均薪酬], 0.75), 0)
return if(竞争力 < 1, (1 - 竞争力) * 100, 0)
var 司龄风险 =
var 司龄 = [平均司龄_月]
return
switch(
true(),
司龄 <= 6, 80,
司龄 >= 36, 60,
30
)
return
考勤风险 * 0.3 + 绩效风险 * 0.3 + 薪酬风险 * 0.25 + 司龄风险 * 0.15

// 15. 九宫格分类
九宫格分类 =
var 绩效 = [平均绩效得分]
var 司龄 = [平均司龄_月] / 12
return
switch(
true(),
绩效 >= 85 && 司龄 >= 2, "明星员工",
绩效 >= 85 && 司龄 < 2, "熟练员工",
绩效 >= 70 && 绩效 < 85 && 司龄 >= 2, "稳定贡献者",
绩效 >= 70 && 绩效 < 85 && 司龄 < 2, "高潜人才",
绩效 < 70 && 司龄 >= 2, "待发展者",
"新人观察"
)

调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
// 用error或blank检查中间结果
测试 =
var a = 10
var b = 0
return
if(b = 0, error("除数为0"), a / b)

// 用selectedvalue看当前筛选上下文
当前选中部门 = selectedvalue('员工信息表'[部门], "多选")

// 用countrows检查表是否为空
表行数 = countrows(filter('员工信息表', '员工信息表'[年龄] > 100))

面试常问

问题 回答要点
calculate和filter区别? calculate改筛选上下文,filter返回表;calculate里可以直接写条件,filter要返回表
all和allselected区别? all清除所有筛选,allselected只清除用户手动筛选,保留视觉交叉筛选
为什么用divide不用/? divide安全除法,除0时返回指定值,不会报错
var什么时候用? 公式复杂、需要重复计算、提高可读性时用
related和lookupvalue区别? related需要建立关系,从多端查一端;lookupvalue不需要关系,直接跨表查找

图表美化

卡片

项目

人力资源

01人才盘点