前言
有些朋友可能已经看过这篇文章,但是经过高人提点,笔者重写了该文章,望能将好的与朋友们分享这些场景。
问题
需求
加班记录如下
员工ID | 日期 | 加班时间 |
---|---|---|
许安-10165 | 2019/1/2 | 4 |
许安-10165 | 2019/1/3 | 15 |
许安-10165 | 2019/1/4 | 17 |
许安-10165 | 2019/1/5 | 19 |
许安-10165 | 2019/1/6 | 5 |
许安-10165 | 2019/1/7 | 13 |
许安-10165 | 2019/1/8 | 22 |
许安-10165 | 2019/1/9 | 22 |
…… | …… | …… |
员工信息如下
员工ID | 员工姓名 | 性别 | 职位 |
---|---|---|---|
曾惠-14485 | 曾惠 | 女 | 管理 |
许安-10165 | 许安 | 女 | HR |
宋良-17170 | 宋良 | 男 | 行政 |
万兰-15730 | 万兰 | 女 | 行政 |
俞明-18325 | 俞明 | 男 | 财务 |
谢雯-21700 | 谢雯 | 男 | 财务 |
康青-19585 | 康青 | 女 | 财务 |
赵婵-10885 | 赵婵 | 女 | HR |
…… | …… | …… |
朋友对各个员工ID的平均加班时间进行分组操作,分成以下24组
组别 | 最小值 | 最大值 |
---|---|---|
0~1 | 0 | 1 |
1~2 | 1 | 2 |
2~3 | 2 | 3 |
3~4 | 3 | 4 |
4~5 | 4 | 5 |
5~6 | 5 | 6 |
6~7 | 6 | 7 |
7~8 | 7 | 8 |
8~9 | 8 | 9 |
9~10 | 9 | 10 |
10~11 | 10 | 11 |
11~12 | 11 | 12 |
12~13 | 12 | 13 |
13~14 | 13 | 14 |
14~15 | 14 | 15 |
15~16 | 15 | 16 |
16~17 | 16 | 17 |
17~18 | 17 | 18 |
18~19 | 18 | 19 |
19~20 | 19 | 20 |
20~21 | 20 | 21 |
21~22 | 21 | 22 |
22~23 | 22 | 23 |
23~24 | 23 | 24 |
现在朋友已经可以按照前文完成相关分组,分组的 DAX 公式如下:
人数(时间动态) =
VAR CurrentItem =
SELECTEDVALUE ( '加班时长区间'[加班时长组别] ) -- 界面所处的当前分组
VAR LeftValue =
CALCULATE ( MIN ( '加班时长区间'[最小值] ), '加班时长区间'[加班时长组别] = CurrentItem ) -- 求当前分组的左端点
VAR RightValue =
CALCULATE ( MIN ( '加班时长区间'[最大值] ), '加班时长区间'[加班时长组别] = CurrentItem ) -- 求当前分组的右端点
VAR UserWithValue =
ADDCOLUMNS (
VALUES ( '加班记录'[员工ID] ),
"平均加班时间", CALCULATE ( AVERAGE ( '加班记录'[加班时长(小时)] ) )
) -- 求每人的平均加班时间
VAR ItemRange =
FILTER ( UserWithValue, [平均加班时间] >= LeftValue && [平均加班时间] < RightValue ) -- 筛选出在当前区间的人
RETURN
COUNTROWS ( ItemRange )
完成后已经可以受打卡日期控制了:
朋友的困扰
但是老板的需求不简单而且也永无至今,老板希望可以看到当前这些人的性别分布状况和职位分布状况,同时能够突出显示制定一个分组或者多个分组的人员性别和职位,但是他发现一个两难的境地:
- 如果在性别和职位中使用度量值人数(日期动态),那么不做任何分组筛选时,性别和职位的值均为空;
- 但是如果使用以下度量值,则性别和职位中人数的统计不能受到加班时长组别的筛选。
人数(静态) =
DISTINCTCOUNT ( '加班记录'[员工ID] )
朋友希望能够使用一个度量值在加班时长组别没有做单独筛选时,计算所有组别,在单独筛选是,仅计算被筛选的组别。
轻松解决
其实问题和简单,其实就是两个度量值人数(静态)和人数(日期动态)的组合,一句话公式轻松搞定:
人数(全动态) =
IF ( HASONEVALUE ( '加班时长区间'[加班时长组别] ), [人数(日期动态)], [人数(静态)] )
终极方案
但是细心的朋友还是发现了这个方法的缺陷,那就是如果要同时要看看多个加班时长组别时,度量值人数(全动态)就会计算所有组别的人数了。
当然办法总是比问题多,仅需构造一个能够受区间组别筛选的度量值即可,直接给出公式
人数(终极动态) =
VAR CurrentItem =
VALUES ( '加班时长区间'[加班时长组别] )
VAR basic_table =
ADDCOLUMNS (
VALUES ( '加班记录'[员工ID] ),
"平均加班时间", CALCULATE ( AVERAGE ( '加班记录'[加班时长(小时)] ) )
)
VAR mid_table =
ADDCOLUMNS (
basic_table,
"分组", CALCULATE (
VALUES ( '加班时长区间'[加班时长组别] ),
FILTER (
ALL ( '加班时长区间' ),
'加班时长区间'[最小值] <= [平均加班时间]
&& '加班时长区间'[最大值] > [平均加班时间]
)
)
)
VAR ItemRange =
FILTER ( mid_table, [分组] IN CurrentItem )
RETURN
COUNTROWS ( ItemRange )
然后将该人数的度量值放入饼图,表格,和柱状图的值中即可。最终效果如下:
不难发现现在对于多个组别的筛选,也是生效的。
详解
- 核心原理是什么?
核心原理就是构造一张虚拟表,用于接受组别的是筛选,以下代码即是构造虚拟表
VAR basic_table =
ADDCOLUMNS (
VALUES ( '加班记录'[员工ID] ),
"平均加班时间", CALCULATE ( AVERAGE ( '加班记录'[加班时长(小时)] ) )
)
VAR mid_table =
ADDCOLUMNS (
basic_table,
"分组", CALCULATE (
VALUES ( '加班时长区间'[加班时长组别] ),
FILTER (
ALL ( '加班时长区间' ),
'加班时长区间'[最小值] <= [平均加班时间]
&& '加班时长区间'[最大值] > [平均加班时间]
)
)
)
其结果就是如下这张虚拟表
- 如何保证能够接受多个分组对其的筛选?
原理很简单,这里使用到了 in 这个函数,所属的代码块如下
VAR ItemRange =
FILTER ( mid_table, [分组] IN CurrentItem )
各位战友对于类似 [分组] = CurrentItem
这样的表达式已经驾轻就熟了,但是如果CurrentItem是一组数值,就无法使用这样的表达式,这时就必须要用in代替。
超越终极
如果各位战友一位这样就结束了,那么就大错特错了,我们的宗旨一直都是极致,在宗老师的提点下,我们又发明了一种新的解法,公式如下
人数(超越终极) =
SUMX ( VALUES ( '加班时长区间'[加班时长组别] ), [人数(日期动态)] )
大家一起来看看效果吧。
那么为什么叫超越终极呢?语句简单是显而易见的,同时有着巨大的性能优势,通过性能分析器可见,在现在仅有10733条记录时,度量值 “人数(超越终极)” 就比度量值 “人数(终极动态)”快 38.6%,在大数据量时,这点差异会被无限放大。
总结
最后,之所以要重写,也是因为在宗老师的提点下发现了新的解法,感觉甚是有趣,故与各位分享。