一. 界面认识与矩阵表操作

1.获取excel数据

菜单栏获取数据/首页从Excel导入数据 -> 选中表 -> 如果数据已清洗完毕可直接点加载,如果没有可点击转换数据进入PowerQuery数据清洗)

左侧菜单栏分别是:报表(可视化)、表格(查看表格内容)、模型

e.g.视觉对象选中饼图,姓名拖放到图例,分数拖放到值

2.菜单按钮

主页常用按钮:获取数据

建模常用按钮:

3.可视化常用的矩阵表(数据透视表)

e.g.行上放班级和姓名,列上放科目,值上放分数

选中报表 -> 点击设置视觉对象格式

#1 放大字体

#2 设置行高

#3 设置自动列宽

#4 筛选

筛选器拖放字段按需进行筛选

#5 居中显示

行标题对齐方式选择居中;特定列 -> 值,对齐方式选择居中

#6 层级加号显示

搜索"+"

#7 行标题缩进

搜索“缩进”

#8 层级分列展示

搜索“渐变”,关闭渐变布局按钮

效果:

#9 行小计和列小计

可选择关闭行小计或列小计,以及修改小计名称

关闭行小计效果:

#10 单元格元素(旧版名称:条件格式)

最常用的是“数据条”,比如可用于制作销售占比、销售金额时,效果:

“图标按钮”效果:

二. 运算符和新建列

1.运算符

运算符

含义

运算符

含义

运算符

含义

+

=

等于

&&

-

>

大于

||

*

<

小于

not

/

>=

大于等于

&

字符串连接

^

<=

小于等于

in

包含

<>

不等于

not in

不包含

2.新建列

打开表格,可以在表工具新建列,也可以在主页找到“新建列”按钮

新建列 -> 新列命名 -> 新列表达式(英文输入法输入单引号即可引用表字段)

引用列的方式:

除了输入单引号,也可以输入中括号。

新的销售量 = [销售数量]*10

新的销售量 = '销售表'[销售数量]*10

两种方法功能相同,什么时候引用列的时候必须带表名呢?

销售金额 = [销售数量]*RELATED('商品'[进价])

=> 在销售表新建销售金额列,[销售数量]来自销售表,[进价]来自商品表,不指明的话,不指明的话表达式不知道去哪张表取[进价]

总结:新建列的时候,当另一个需要计算的列来自其他表时,一定要指明表名。

为了避免出错,以后都采用单引号的方式引用列,在任何情况下都指明表名。

三. 建模与关系函数

1.建模

导入多张表的时候要考虑模型:

-- 连线时建议把1端的表放上面,多端的表放下面

1对多的关系:一个商品只会有一种进价,但是会有多笔销售记录

箭头方向(传递方向):商品表指向销售表,代表商品表可以筛选销售表,销售表可以向商品表索取内容

=> SQL中的表关联

2.LOOKUPVALUE函数

-- 就是VLOOKUP

语法:LOOKUPVALUE(把哪张表的哪个列拿过来, 找那张表上的谁, 找自己表里的谁)

LOOKUPVALUE('A'[c], 'A'[a], 'B'[b])

-- 通过B表的b列关联A表的a列,从而获取A表中a对应的c列值

-- 通常用多端的表去V1端的表

e.g. 单价 = LOOKUPVALUE('商品表'[进价], '商品表'[品名], '销售表'[商品名称])

删除列:

右键列名,选择删除

3.RELATED函数和RELATEDTABLE函数

(1) RELATED函数:(多端找一端)(事实表找维度表)(数据表找基础表)

销售成本 = '销售表'[销售数量]*RELATED('商品表'[进价])

-- 销售数量来自销售表,销售表是多端;进价来自商品表,商品表是1端;销售表向商品表索取[进价]

e.g.实现商品表筛选销售表,比如查看商品表中的商品各卖了多少笔记录

订单数量 = COUNTROWS(RELATEDTABLE('销售表'))

使用RELATEDTABLE效果:

-- 每一个品名对应2笔订单

RELATED是多端找一端的引路人,RELATEDTABLE是一端找多端的引路人。

四. 度量值、CALCULATE引擎、CALCULATETABLE筛选表

1.度量值

新建度量值:

度量值体现筛选功能:

矩阵表 -> 行放日期(下拉选择日期,取消日期层次结构) -> 值放总销量

-- 度量值是不占内存的,需要的时候拿出来用;如果是新建列,数据量大的话很占内存

-- 度量值最大的优点是自带筛选功能;新建列是没有筛选功能的

-- 度量值在引擎中可以循环使用

2.筛选引擎CALCULATE

CALCULATE(表达式,[筛选器1],…)

在筛选器修改的上下文中对表表达式进行求值

-- 第一个参数是计算器,第二个参数是筛选器,筛选器可缺省

销售表新建3个度量值:

总销量 = SUM('销量表'[销售数量])

A产品销量1 = CALCULATE([总销量],'商品表'[品名]="A")

A产品销量2 = CALCULATE([总销量],'销售表'[商品名称]="A")

可视化界面查看:

矩阵表 -> 行上放商品表的品名,值上放销售表的总销量、A产品销量1、A产品销量2

-- 表达式使用销售表的商品名称时显示正确

-- 原因是商品表1对多指向销售表,商品表筛选销售表;所以行上放商品表时,筛选器要用销售表作为条件;如果筛选器想用商品表品名作为条件,则行上换成销售表的商品名称,同样可以实现功能

3.多条件CALCULATE与筛选表

(1)多条件筛选CALCULATE

-- 不同列

多条件 = CALCULATE([总销量],'商品表'[品名] = "A",'商品表'[进价] = 0.1)

-- 相同列

多条件1 = CALCULATE([总销量],'商品表'[品名] in {"A","B","C"})

多条件2 = CALCULATE([总销量],not '商品表'[品名] in {"A","B","C"})

(2)筛选表

CALCULATETABLE(表,筛选条件)

-- 与CALCULATE区别在于,可以多表运作筛选,最终返回一张表

e.g.

新表 = CALCULATETABLE('销售表','商品表'[品名]="A",'商品表'[进价]=0.1)

-- 返回销售表的内容,根据商品表的条件去筛选它

-- 新建表会出现在模型视图

4.度量值的存放

-- 不建议在每张表到处建,会很乱,建议存放在统一的地方

主页 -> 输入数据 -> 修改名称为“度量值”-> 加载 -> 在“度量值”表格中新建度量值 -> 表格里有度量值之后删除空列

五. 高级筛选器FILTER与VALUES人工造表

-- 返回一个表,用于表示另一个表或表达式的子集,不能单独使用

FILTER函数对筛选的表进行横向的逐行扫描,这样的函数叫迭代函数。

FILTER是一个表函数,以后如果遇到表函数不知道怎么用的话,可以先放到新建表里测试一下,FILTER效果:

使用举例:

COUNTROWS(FILTER(表,筛选条件))

CALCULATE(表达式(度量值),FILTER(表,筛选条件))

-- 导入课件材料后发现“花名册”的列名都是“COLUMN”,第一行数据才是可识别的列名

右键“花名册” -> 编辑查询 -> 将第一行提作标题 -> 关闭并应用

由于两张表都有列名“学号”,所以在模型视图自动建立了连接

1.效果相同,为什么使用FILTER函数

总分 = SUM('成绩表'[分数])

1班男生1 = CALCULATE('度量值'[总分],'花名册'[班级]="1",'花名册'[性别]="")

1班男生2 = CALCULATE('度量值'[总分],FILTER('花名册','花名册'[班级]="1"&&'花名册'[性别]=""))

报表视图选择矩阵表,值上放“1班男生1”和“1班男生2”效果:

-- 两个公式效果是等价的

2.什么时候使用FILTER函数

在CALCULATE函数中的直接筛选条件里,我们只能输入:

'表'[列] = 固定值 (或>, <, >=, <=, <>, in, not in固定值)

但是遇到如下情况,就要使用FILTER函数:

[]=[度量值][]=[公式][]=[]

[度量值]=[度量值][度量值]=[公式][度量值]=固定值

举例:总分大于250分的学生一共考了多少分?

FILTER总分1 = CALCULATE('度量值'[总分],FILTER('花名册','度量值'[总分]>250))

FILTER总分2 = CALCULATE('度量值'[总分],FILTER('成绩表','度量值'[总分]>250))  --错误

矩阵表行上放姓名,值上放“FILTER总分1”和“FILTER总分2”效果:

“FILTER总分2”错误原因:

FILTER有一个严格要求,FILTER第一参数的表必须是唯一值的表,不能是多端数据的表

如果我只有一张表怎么办?我没有唯一表,只有数据表!那就自己造一张表!

=> VALUES人工造表

3.VALUES人工造表

FILTER总分3 = CALCULATE('度量值'[总分],FILTER(VALUES('成绩表'[学号]),'度量值'[总分]>250))

VALUES也是表函数,拿到新建表里做测试:

-- 提取出了成绩表中“学号”的唯一值

矩阵表值上加上“FILTER总分3”效果:

-- 效果和“FILTER总分1”一致

经典语句:CALCULATE([度量值],FILTER(VALUES('表'[列名]),筛选条件))

六. 被翻译耽误的上下文

1.上下文

同样一个公式,写在[新建列]和[度量值]中效果不一样。

-- 新建度量值

总分 = SUM('成绩表'[分数])

矩阵表行放[学号],值放[总分]效果:

-- 每个人都可以实现筛选功能

-- 在成绩表新建列

总分列 = SUM('成绩表'[分数])

-- 每一行都显示新建列表达式的结果,即整张成绩表的总分

这就是上下文,新建列是行上下文(逐行扫描,数求出来之后每一行都显示这个数),而度量值是筛选上下文(数值上可以进行筛选)。

如果想让新建列实现筛选功能需要套上CALCULATE:

总分列2 = CALCULATE(SUM('成绩表'[分数]))

总结:

(1) 度量值天生具有筛选功能

(2) 新建列是行上下文,行上下文没有筛选功能

(3) 想让行上下文实现筛选功能就要在外面套一个CALCULATE

2.FILTER与上下文

FILTER总分1 = CALCULATE('度量值'[总分],FILTER('花名册','度量值'[总分]>250))    #正确

FILTER总分4 = CALCULATE('度量值'[总分],FILTER('花名册',SUM('成绩表'[分数])>250))    #错误

矩阵表行上放班级,值上放“FILTER总分1”和“FILTER总分4”,效果:

-- FILTER4显示的是1班所有人的成绩、2班所有人的成绩、3班所有人的成绩以及总累计

-- 原因是SUM('成绩表'[分数])不是度量值,它只是一个聚合函数,只起到了计算的功能,不具备筛选功能

如果一定要采取公式的写法实现筛选功能,套上CALCULATE:

FILTER总分5 = CALCULATE('度量值'[总分],FILTER('花名册',CALCULATE(SUM('成绩表'[分数]))>250))

所谓的筛选上下文,就是度量值,度量值自带天然的CALCULATE函数;

没有筛选功能的新建列就是行上下文,如果行上下文向转成筛选上下文,它是不会自动转的,要手动套上一个CALCULATE函数。

七. ALL函数、ALLEXCEPT函数、ALLSELECTED函数

ALL函数

作用:清除筛选 => 指的是清除度量值的筛选功能,因为度量值天生就具备筛选功能

返回:清除筛选后的表格或列

语法:

ALL(表)

ALL(表[列])

新建度量值:

商品表中商品总数 = COUNTROWS('商品表')

销售表中商品数量 = CALCULATE(COUNTROWS('商品表'),'销售表')

-- 注意,此处CALCULATE的第二参数是表,表示从商品表中筛选出销售表中的商品;销售表的商品是包含在商品表里的,此处计算器计算商品总数,筛选条件是销售表

不能筛选的总数 = COUNTROWS(ALL('商品表'))

矩阵表行上放“商品编码”,值上放“商品表中商品总数”、“销售表中商品总数”、“不能筛选的总数”,效果:

如果行上放了“商品编码”,想要计算销售表的商品数量占商品表商品总数量的占比(虽然没有意义,要么1/26要么0/26),去除以“商品表中商品总数”是无法实现的,因为度量值自带筛选功能,每行的“商品表中商品总数”会根据“商品编码”筛选计算出来都是1,最终占比要么1/1要么0/1。“不能筛选的总数”使用ALL清除度量值的筛选功能,才能得到正确的占比。

占比 = '度量值'[销售表中商品数量]/'度量值'[不能筛选的总数]

案例一【应用于表】:计算销售表中每个商品的占比

总销量 = SUM('销售表'[销售数量])

禁止筛选的总销量 = CALCULATE('度量值2'[总销量],ALL('销售表'))

每个商品的占比 = '度量值2'[总销量]/'度量值2'[禁止筛选的总销量]

矩阵表行上放'销售表'[商品编码]:

-- ALL函数用得最多的地方就是计算占比的时候

报表视图添加切片器,筛选字段选择'销量表'[日期],设置日期区间为12.1-12.1,效果:

-- 切片器:用于过滤数据,实现数据动态展示

-- “总销量”仅展示12.1各产品销量汇总,“禁止筛选的总销量”还是展示的12.1+12.2的全产品总销量

-- 现希望总计占比保持100%

总结:

ALL函数参数为表时,忽略所有的筛选条件,无论是该图表内还是外部切片器。

ALLSELECTED函数替换ALL函数解决占比问题:

-- 将“禁止筛选的总销量”表达式中ALL替换为ALLSELECTED

禁止筛选的总销量 = CALCULATE('度量值2'[总销量],ALLSELECTED('销售表'))

-- 替换后切片器的筛选对“禁止筛选的总销量”生效,仅展示12.1的全产品总销量

-- ALLSELECTED除了保留来自外部的筛选器,和ALL没有任何区别,多用于显示百分比的时候

以上图例中,行上放的是'销售表'[商品编码],如果想放商品名称,换成'商品表'[商品名称]:

-- 部分产品如A在销售表是没有销量的,但是因为ALL函数是不受内部筛选影响的,所以“禁止筛选的总销量”列都显示了值

解决方法有2种:

(1) 清除矩阵表值上的“禁止筛选的总销量”

(2) 到销售表里把商品表的商品名称VLOOKUP过来

销售表新建列:

商品名称 = LOOKUPVALUE('商品表'[商品名称],'商品表'[商品编码],'销售表'[商品编码])

报表视图矩阵表行上放'销售表'[商品名称]:

案例二【应用于列】:

-- 由于销售表中没有大类这个列,我们先把大类V过来再测试

【销售表新建列】

大类 = LOOKUPVALUE('商品表'[大类],'商品表'[商品编码],'销售表'[商品编码])

【销售表新建列】

规格 = LOOKUPVALUE('商品表'[规格],'商品表'[商品编码],'销售表'[商品编码])

【度量值】

取消列筛选 = CALCULATE('度量值2'[总销量],ALL('销售表'[规格]))

报表视图行上放'销售表'[大类],值上放'度量值2'[总销量]、'度量值2'[取消列筛选];添加切片器,字段添加'销售表'[日期](视觉对象格式 -> 切片器设置 -> 选项 -> 样式选为垂直列表);添加切片器,字段添加'销售表'[规格]:

-- 用“日期”筛选时,“取消列筛选”值发生变化,即外部筛选器生效

-- 用“规格”筛选时,“取消列筛选”值保持不变,即外部筛选器不生效

总结:

当ALL函数参数为列时,忽略该列筛选,图表内部其他字段或外部筛选其他字段会产生作用。

注意:

ALL函数在引用列的时候,必须与矩阵表的行和列在同一张表。

比如矩阵表的行和列都来自于销售表,那么ALL函数引用列的时候,也必须引用销售表的列,like ALL('销售表'[规格]),不能从商品表拿规格,必要时使用LOOKUPVALUE先把需要的字段V过来。

ALL函数引用列的使用场景:

【新建度量值】

占比 = '度量值2'[总销量]/'度量值2'[取消列筛选]

矩阵表行上放'销售表'[大类]、'销售表'[规格],值上加上'度量值2'[占比]:

-- 展示袋装占比、盒装占比 -> 可用于计算分类占比

ALLEXCEPT函数:除之外

语法:ALLEXCEPT('表名'[列名])

-- 除了参数列以外的列,全部取消筛选

-- 等同于ALL('表名'[列名1],'表名'[列名2],'表名'[列名3],…)

八. ALLNOBLANKROW核对数据

-- 返回表中除空白行以外的所有行或列中的所有值

【新建表】

测试表1 = ALLNOBLANKROW('子表')

-- 子表中无重复的行或空白行,所以全部返回

测试表2 = ALLNOBLANKROW('子表'[姓名])

-- 返回子表中去重后的所有姓名

-- 返回:去重(即便只有一列,它也是表)

【新建度量值】

行数1 = COUNTROWS('子表')

行数2 = COUNTROWS(ALL('子表'))

行数3 = COUNTROWS(ALLNOBLANKROW('子表'))

矩阵表行上放'子表'[姓名],值上放'度量值2'[行数1]、'度量值2'[行数2]、'度量值2'[行数3]:

把行换成'父表'[姓名]:

-- 出现空行,因为父表里没有唐僧

【案例】

【新建度量值】

总销量 = SUM('销售表'[销售数量])

ALL总销量 = CALCULATE('度量值'[总销量],ALL('商品表'))

ALLNOBLANKROW总销量 = CALCULATE('度量值'[总销量],ALLNOBLANKROW('商品表'))

矩阵表行上放'销售表'[名称],值上放3个度量值:

-- 键盘存在于销售表中,但不存在于商品表中,通过ALLNOBLANKCOUNT一眼看出哪些东西是商品表没有但是被卖出去的

-- 一般人工造出来的数据才会发生这种情况,计算机记录的数据是不会发生的

九. 聚合函数和迭代函数

1.聚合函数

语法:

-- 参数是物理列

总钱数 = SUM('表'[捡钱])

平均钱数 = AVERAGE('表'[捡钱])    -- 算出平均值

-- AVERAGE是数值总和除以数值的个数,AVERGAEA是数值总和除以项数

最大值 = MAX('表'[捡钱])

最小值 = MIN('表'[捡钱])

记录数 = COUNTROWS('表')

人数 = DISTINCTCOUNT('表'[姓名])    -- 去重计数

COUNTA函数:计算列中单元格不为空的数目

COUNTBLANK函数:计算列中单元格为空的数目

PRODUCT函数:计算列中单元格乘积

2.迭代函数

【新建列】

净值 = ''[捡钱]-''[丢钱]

= SUMX('',''[捡钱]-''[丢钱])

2 = CALCULATE(SUMX('',''[捡钱]-''[丢钱]))

计算过程:

对每一行逐行扫描,比如

第一行,1-0.1=0.9

第二行,2-0.9=1.1

第五行,5-0.8=4.2

将所有值求和。因为新建列是行上下文,不具备筛选功能,所以上图所有行对应的[列]都是最后的的总和12.8。

如果没有SUMX函数:

净值  =  '捡钱'-'丢钱'

求和 = SUM('净值')

语法:SUMX(表,算术表达式)

解释:将每一行按算术表达式计算后,再将计算结果求和。

AVERAGEX、MAXX、MINXX、COUNTX、COUNTAX、PRODUCTX……它们和FILTER函数一样都是行上下文函数

十. EARLIER函数【当前行】

案例一:计算下一个订单日期

【新建列】

下个订单日期 = SUMX(FILTER('',''[序号]=EARLIER(''[序号])+1),''[销售日期])

分析:

EARLIER('表'[序号])+1  

-- 当前行序号+1

FILETER('表','表'[序号]=EARLIER('表'[序号])+1)  

 -- 将“序号等于当前行序号+1”的表筛选出来

SUMX(FILETER('表','表'[序号]=EARLIER('表'[序号])+1),'表'[销售日期])   

-- 这里用哪个迭代函数都可以,没有求和求平均的操作,仅用来迭代返回销售日期(不理解)

案例二:累计求和

累计求和 = SUMX(FILTER('',''[序号]<=EARLIER(''[序号])),''[销售量])

分析:

将“序号小于等于当前行序号”的表筛选出来,因为肯定不止一行,所以用SUMX迭代求和。

案例三:分组累计求和

商品累计求和 = SUMX(FILTER('',''[序号]<=EARLIER(''[序号])&&''[商品]=EARLIER(''[商品])),''[销售量])

分析:

将“序号小于等于当前行序号”且“商品等于当前行商品”的表筛选出来,求和。

表中没有序号,可以用日期,比如:累计计算电视每天的累加值,手机每天的累计值:

商品累计求和 = SUMX(FILTER('',''[销售日期]<=EARLIER(''[销售日期])&&''[商品]=EARLIER(''[商品])),''[销售量])

案例四:累计购买次数

第几次购买 = COUNTROWS(FILTER('1','1'[姓名]=EARLIER('1'[姓名])&&'1'[序号]<=EARLIER('1'[序号])))

分析:

将“序号小于等于当前行序号”且“姓名等于当前行姓名”的表筛选出来,计算行数。

十一.  VALUES与DISTINCT区别

1.VALUES函数

VALUES(表[列]):

表1 = VALUES('表'[姓名])    -- 把姓名不重复的列提取出来,形成一个单列的表;人造基础表(单列)

VALUES(表):

表2 = VALUES('表')    -- 返回表的所有行,就是复制一张表;人造虚拟表,建立虚拟关系的时候用到

2.DISTINCT函数

DISTINCT(表名[字段名])    -- 返回:去重后,唯一值的列

DISTINCT(表名)    -- 返回:只包含非重复行的表

DISTINCT(返回表的表达式)

2 = DISTINCT(FILTER('1','1'[性别]=""))

3.区别

(1) 对于空白行的处理上,VALUES包括没有匹配的空白行,DISTINCT不返回没有匹配的空白行

空白行的专业术语:参照完整性不匹配

=> 说人话:销售表上有的数据,基础表上没有;比如销售表卖出了A04商品,商品表没有A01(现实中不可能发生)

【新建度量值】

行数 = COUNTROWS('商品表')

VALUES编码 = COUNTROWS(VALUES('商品表'[商品编码]))

DISTINCT编码 = COUNTROWS(DISTINCT('商品表'[商品编码]))

VALUES进价 = COUNTROWS(VALUES('商品表'[进价]))

DISTINCT进价 = COUNTROWS(DISTINCT('商品表'[进价]))

矩阵表值上放5个度量值:

-- VALUES出现了空白行,DISTINCT正确

-- 因为在模型视图中,商品表和销售表建立了关系,A04不在商品表里,但在销售表里,VALUES也会把它算作一行;DISTINCT排除该问题,不返回

(2) DISTINCT函数允许列名或任何有效的表表达式作为其参数,但VALUES函数仅接受列名或表名作为参数

十二. 条件判断函数【基础】

1.IFERROR【遇到错误时使用指定数值替换】

 

销售金额 = IFERROR('1'[销售数量]*'1'[单价],BLANK())

-- 如果表达式遇到错误则返回空

销售金额 = IFERROR('1'[销售数量]*'1'[单价],"不能计算")    错误!!!

-- IFERROR只能返回空或者数值

销售金额 = IFERROR('1'[销售数量]*'1'[单价],0)

2.IF条件判断

IF(条件, A, [B])

检查是否满足条件,如果为 TRUE 则返回A,如果为 FALSE 则返回B;

B可以省略不写,省略时返回为空,比如满足某个分数时返回“优秀”,不满足的时候不写。

IF适合条件比较少的时候使用,如果条件太多,嵌套太多,不方便使用。

案例一:

【新建列】

称呼 = IF('2'[性别]="","先生","女士")

案例二:

间隔 = IF('5'[取款日期]=BLANK(),BLANK(),'5'[取款日期]-'5'[存款日期])

-- 默认返回日期类型,列工具 -> 数据类型 -> 整数,即可显示为整数

3.SWITCH多项条件判断

SWITCH(表达式, 值1, 结果1, 值2,  结果2, ..., [else])

根据表达式的值返回不同结果;最后一个参数可以省略不写,代表以上值都不满足会返回什么,省略时返回为空。

案例:

月份 = SWITCH('3'[],

1, "1", 2, "2",

3, "3", 4, "4",

5, "5", 6, "6",

7, "7", 8, "8",

9, "9", 10, "10",

11, "11", 12, "12",

"未能识别")

SWITCH特殊用法:

使用TRUE作为第一参数的作用是,返回条件判断列表中第一个为TRUE的结果。

-- 如果是多个字段同时判断:'例4'[年龄]<30 && '例4'[性别]="男"

年龄段 = SWITCH(TRUE(),

'4'[年龄]<30,"30岁以下",

'4'[年龄]<40,"30-40",

'4'[年龄]<50,"40-50",

"50岁以上")

-- 比如35,第一个为TRUE是在判断<40的时候,所以返回“30-40岁”,后面的条件不看了

十三. 安全除法DIVIDE与按层级计算ISINSCOPE函数

1.安全除法

语法:DIVIDE(分子,分母,[替换结果])

替换结果可以省略不写,省略时返回为空。

除法 = [分子]/[分母]

-- 分母为0 时返回无穷大

安全除法 = DIVIDE(分子,分母,BLANK())

安全除法 = DIVIDE(分子,分母,0)

2.按层级计算ISINSCOPE函数

ISINSCOPE函数的意思:是否在范围内

官方释义:当指定的列是级别层次结构中的级别时,返回true。

【新建度量值】

层级占比 = SWITCH(TRUE(),

ISINSCOPE('商品表'[商品名称]),DIVIDE('度量值'[总金额],CALCULATE('度量值'[总金额],ALL('商品表'[商品名称]))),

ISINSCOPE('商品表'[产品类别]),DIVIDE('度量值'[总金额],CALCULATE('度量值'[总金额],ALL('商品表'[产品类别]))),

DIVIDE('度量值'[总金额],CALCULATE('度量值'[总金额],ALL('商品表'[商品名称])))

)

矩阵表行上放('商品表'[产品类别]'商品表'[商品名称] -> 产生了层级关系,值上放'度量值'[层级占比]:

-- 判断当前行是否属于[产品类别]层级,如果是,则计算该产品类别总销量占所有产品类别总销量的占比;比如:手机类总销量占所有产品类别总销量的46%

-- 判断当前行是否属于[产品名称]层级,如果是,则计算该产品名称总销量占其父层级下所有产品总销量的占比;比如:小米10占手机类产品总销量的7%

-- 如果既不是[产品名称]级别,又不是[产品类别]级别,则直接计算(可以不写,如果不写,总计行为空)

-- 层级关系跟数据源表没有关系,主要取决于拉矩阵的时候行那一栏怎么放

十四. ISCROSSFILTERED函数和ISFILTERED函数的区别

语法:

ISFILTERED(TableNameOrColumnName)

在存在针对指定列的直接筛选器时,返回 true。

-- 只有对指定列筛选时才返回true

ISCROSSFILTERED(TableNameOrColumnName)

在对指定表或列进行交叉筛选时,返回 true。

-- 对表中任意列进行筛选都会返回true

【新建表】

日期表 = ADDCOLUMNS(

CALENDAR(DATE(2019,1,1),DATE(2021,12,31)),

"",YEAR([Date]),

"",MONTH([Date]),

"",WEEKNUM([Date]),

"年季度",YEAR([Date])&"Q"&ROUNDUP(MONTH([Date])/3,0),

"年月",YEAR([Date])*100+MONTH([Date]),

"年周",YEAR([Date])*100+WEEKNUM([Date]),

"星期几",WEEKDAY([Date])

)

 

【新建度量值】

ISFILTERED测试 = ISFILTERED('日期表'[Date])

ISCROSSFILTERED测试 = ISCROSSFILTERED('日期表'[Date])

报表视图添加切片器,字段使用'日期表'[年月];添加矩阵表,值上放2个度量值:

-- 不筛选年月

-- 筛选年月

# 因为没有对指定的'日期表'[Date]做筛选,所以“ISFILTERED测试”为FALSE

# 因为对'日期表'做了筛选,所以“ISCROSSFILTERED”为TRUE

案例:实现切片器筛选日期后,展示指定日期倒推12个月的销量数据

【日期表新建列】

年月序号 = SWITCH(

TRUE(),

'日期表'[]=2019,0+'日期表'[],

'日期表'[]=2020,12+'日期表'[],

'日期表'[]=2021,24+'日期表'[]

)

【新建度量值】

12个月 = IF(

ISCROSSFILTERED('日期表'[年月]),

CALCULATE(VALUES(''[年月]),FILTER('',''[年月序号]<=MAX('日期表'[年月序号])&&''[年月序号]>MAX('日期表'[年月序号])-12)),

1

)

-- CALCULATE第一参数可以是一张表,表是计算器,后面筛选这张表的条件就是筛选器

-- VALUES('表'[年月])得到表里年月不重复的值

-- 使用FILTER的原因是需要列和列的比较,CALCULATE默认的筛选器只支持和固定值的比较

-- 不理解这里MAX的作用,尝试不使用MAX直接和'日期表'[年月序号]比较,发现Power BI无法找到'日期表'及其字段(原因未知),且手动输入后报错:

-- 不能直接对比的原因:[年月]在'日期表'中不是唯一值,即使'日期表'过滤出一个[年月]值,还是会对应多个[年月序号](比如选择201901,在日期表中对应了多行数据,也就意味着有多个年月序号,虽然它们的[年月序号]相同,都是1),公式无法确定单个值

-- MAX换成MIN、AVERAGE是一样的效果

报表视图添加切片器,字段使用'日期表'[年月];添加矩阵表,行上放'表'[年月](只能放'表'里的,不能放'日期表'里的,因为两张表没有连线),值上放'表'[销售金额];选中矩阵表 -> '度量值'[12个月]放到筛选器里 -> 条件为“不为空” -> 应用筛选器

-- 外部筛选器[前12个月]条件设置成“不为空”:切片器选择201903,则矩阵表值显示201901-201903的销量数据,不满足范围的就不显示

十五. FIRSTNONBLANK与LASTNONBLANK函数

-- FIRSTNONBLANK函数与LASTNONBLANK函数属于表函数,有些时候也可以作为值函数使用

语法:

FIRSTNONBLANK(<列>,<表达式>)

返回列中表达式具有非空白值的第一个值

LASTNONBLANK(<列>,<表达式>)

返回列中表达式具有非空白值的最后一个值

<列>:任何列,或者具有单列的表,也可以是表达式

<表达式>:计算空值的表达式,也就是判定条件

返回结果:单列的表,只有一行数据(也就是只有一个返回值,只不过这个值在表中)

案例一:应用于表函数

【新建

2 = FIRSTNONBLANK(''[日期],CALCULATE(SUM(''[销售])))

-- 求出产生第一笔销售额的日期

-- 表达式可以用度量值或者嵌套CALCULATE,否则会导致计算忽略计算筛选上下文,只考虑行上下文,那么呈现的结果将是2020年1月1日(最小的日期);即不套CALCULATE,每一行对应的表达式值就都是SUM起来的总销售,不是根据日期和姓名筛选后的了

总结:第二个参数的表达式要具备筛选功能

案例二:应用于值函数

-- 求出每个人第一笔销售的销售额

【新建度量值

每个人第一个不为空的销售 = CALCULATE(

SUM(''[销售]),

FIRSTNONBLANK('表'[日期],CALCULATE(SUM('表'[销售])))

)

-- 将案例一的表放到CALCULATE筛选器里,用于筛选每个人第一个有销售的日期所产生的销售额

矩阵表行上放'表'[姓名],值上放'度量值'[每个人第一个不为空的销售]

十六. 进阶条件判断函数

1.HASONEFILTER函数【判断是否被筛选】

语法:HASONEFILTER(列名)

HASNOFILTER是被直接筛选影响的。

当指定的列有且只有一个由直接筛选产生的值时,返回true。参数必须使用物理列(表里有什么列就用什么列,可以新建列),不支持表达式。

案例一:

【新建度量值】

总分 = SUM('案例1'[成绩])

HASONEFILTER总成绩 = IF(HASONEFILTER('案例1'[姓名]),[总分],BLANK())

矩阵表行上放'案例1'[姓名],值上放'案例1'[成绩];切片器字段上放'案例1'[姓名];新矩阵表行上放'案例1'[老师],值上放'案例1'[HASONEFILTER总成绩]

-- 指定的列有且只有一个由直接筛选产生的值,HASONEFILTER返回true,IF语句返回总分

-- 指定的列有多个由直接筛选产生的值,HASONEFILTER返回false,IF语句返回BLANK()

案例二:

【新建度量值】

总金额 = SUM('案例2'[销售金额])

-- 如果'案例2'[销售金额]是外汇

外汇转人民币 = IF(HASONEFILTER('汇率'[货币]),FIRSTNONBLANK('汇率'[汇率],1)*'案例2'[总金额],BLANK())

-- 此处的FIRSTNONBLANK函数由于只需要取第一个不为空的值,不牵涉表达式,这种情况第二个参数可以写1,也可以写两个英文双引号"",都代表第二个条件不要,什么都不做,只取汇率里面的第一个

-- 效果:只可以选择一种货币类型进行汇率转换,如果同一个货币类型有多个汇率值,则取第一个;不支持选择多种货币类型进行汇率转换,如果选了多个,则返回空

切片器字段上放'汇率'[货币]用于筛选,矩阵表行上放'案例2'[日期](下拉选择日期,取消层次结构),值上放'案例2'[总金额]、'案例2'[外汇转人民币]

-- -- 如果'案例2'[销售金额]是人民币

人民币转外汇 = IF(HASONEFILTER('汇率'[货币]),DIVIDE('案例2'[总金额],FIRSTNONBLANK('汇率'[汇率],1)),BLANK())

2.HASONEVALUE函数【判断是否只有一行数据】

语法:HASONEVALUE(列名)

通常和IF函数搭配使用,判断某列是否只有一行数据。

经典语句:IF(HASONEVALUE('表名'[列名]),[度量值],BALNK())

案例:

-- 只有一个学生的老师,他们的学生考了多少分

【新建度量值】

总分 = SUM('案例1'[成绩])

姓名唯一的人的分数 = IF(HASONEVALUE('案例1'[姓名]),'案例1'[总分],BLANK())

矩阵表行上放'案例2'[老师],值上放'案例2'[姓名唯一的人的分数]

-- 因为李小龙和叶问这两个老师是唯一的没有重复的,只对应一个学生,所以只显示他们的分数

-- 总计不显示,一般当不想要总计的时候,才会用HASONEVALUE函数,平时用不到

【扩展知识】DATATABLE人工建表

手工建表 - 菜单栏“输入数据“

参数表1 = DATATABLE("字段名",数据类型,{{数据1},{数据2}})

数据类型:INTEGERDOUBLESTRINGBOOLEANCURRENCYDATETIME

多字段这样写:

参数表2 = DATATABLE("字段名1",数据类型1,"字段名2",数据类型2,{{数据11,数据12},{数据21,数据22}})

-- 数据11和数据22分别是字段1和字段2的值,以行显示;数据11和数据12是第一行,数据21和数据22是第二行

3.SELECTEDVALUE函数

当指定列只有一个值时返回该值,否则返回替代结果,省略替代结果时返回空值

语法:

SELECTEDVALUE('表名'[列名],[替代结果(省略返回空)])

案例:

-- 实现切片器选择销售量时,报表显示销售量;切片器选择销售额时,报表显示销售额

【新建表】

参数表 = DATATABLE("字段名",STRING,{{"销售量"},{"销售额"}})

切片器字段上放'参数表'[字段名],簇状柱形图X轴放'案例3'[商品名称],Y轴放'案例3'[销售量]、'案例3'[销售金额]

-- 此时切片器选择销售额,柱形图依旧同时显示销售额和销售量

【新建度量值】

= SWITCH(SELECTEDVALUE('参数表'[字段名]),"销售额",SUM('案例3'[销售金额]),"销售量",SUM('案例3'[销售量]))

簇状柱形图Y轴换成'案例3'[值]:

十七. 转换函数

1.CURRENCY函数【数字转货币】

作用:将表达式结果转换为货币类型

【新建列】

列 = CURRENCY('CURRENCY'[数值1])

-- "文本1"数据类型改成文本,"日期"数据类型改成日期

2.INT函数【向下舍入】

作用:将表达式转换为整数(向下舍入

说人话:

当数值为正数时,取整数抹

当数值为负数时,凑零补整

【新建列】

= INT('INT'[数值])

3.TRUNC函数【直接取整】

【新建列】

= TRUNC('TRUNC'[数值])

4.ROUND函数【四舍五入】

语法:

ROUND(数值,保留小数点的位数)

【新建列】

= ROUND('ROUND'[数值],4)

5.MROUND函数【取数值的倍数】

语法:

MROUND(数值,哪个数的倍数)

-- 两个参数的小数点位数必须一致

-- 符号必须一致

MROUND(1.3,0.2) => 1.4

-- 从1.3开始往上找0.2的倍数,最近的是1.4

MROUND(-10,-3) => -9

-- 从-10开始往上找-3的倍数,最近的是-9

MROUND(5,-2) => ERROR

【新建列】

= MROUND('MROUND'[数值],5.00)

-- 在6.05附近找2.00的倍数,最近的是6;在7.05附近找2.00的倍数,最近的是8???

6.日期和时间的转换

DATE(年,月,日)

TIME(小时,分钟,秒)

【新建列】

= DATE('Date'[],'Date'[],'Date'[])

-- 数据类型选择日期

= TIME('Time'[],'Time'[],'Time'[])

7.CONVERT函数【转换为指定数据类型】

语法:

CONVERT(表达式,数据类型)

数据类型:

INTEGER整型、DOUBLE双精度、STRING字符、BOOLEAN布尔、CUREENCY货币、DATETIME日期时间

【新建列】

= CONVERT('CONVERT'[销售数量]*'CONVERT'[单价],STRING)

8.DATEVALUE函数【文本格式的日期转成日期时间格式的日期】

【新建列】

= DATEVALUE('DATEVALUE'[文本日期])

-- 数据类型改成日期,否则带时间,因为默认转成日期时间格式

9.TIMEVALUE函数【文本格式的时间转成日期时间格式的时间】

【新建列】

2 = TIMEVALUE('Time'[]

-- 为了测试,列的数据类型改成文本

-- 列2数据类型改成日期,否则带日期,因为默认转成日期时间格式

10.VALUE函数【文本转数值】

语法:

VALUE(<文本>)

返回结果:数值类型,十进制数字

-- 如果文本不是数字、日期或时间格式,则返回错误。

【新建列】

6 = VALUE('CURRENCY'[文本1])

十八. FORMAT函数【格式化】

语法:

FORMAT(数值或日期,格式)

将值转换为指定格式的文本,通常与DATETIME格式一起使用

【新建列】

= FORMAT(DATE('Date'[],'Date'[],'Date'[]),"yyyymmdd")

具体格式详见官方文档:

FORMAT 函数 (DAX) - DAX | Microsoft Learn

注意几个特殊情况:

1.在格式中输入的字符串要加两个双引

= FORMAT(DATE('Date'[],'Date'[],'Date'[]),"""孙兴华"" yyyy")

2.转义字符

= FORMAT(DATE('Date'[],'Date'[],'Date'[]),"yyyy \Qq")

-- Q代表季度,属于格式关键字,想要显示"Q"需要加转义字符反斜杠\,否则Qq会显示两遍季度

3.自定义格式

= FORMAT(DATE('Date'[],'Date'[],'Date'[]),"dd/mm/yyyy")

= FORMAT(TIME('Time'[],'Time'[],'Time'[]),"h:nn:ss")

十九. 日期时间函数【非智能函数】

1.提取年月日时分秒、季度、当前时间

【新建列】

= YEAR(''[时间日期])

= MONTH(''[时间日期])

= DAY(''[时间日期])

= HOUR(''[时间日期])

= MINUTE(''[时间日期])

= SECOND(''[时间日期])

季度 = QUARTER(''[时间日期])

当前日期和时间 = NOW()

当前日期 = TODAY()

-- 可修改当前日期的数据类型为“日期”

2.星期和周

【新建列】

当周的第几天 = WEEKDAY(''[时间日期],2)

-- 从星期一开始算做一周的开始

不同国家对周几算当周的开始有不同的规定:

-- 第二个参数 = 1

-- 第二个参数 = 2

-- 第二个参数 = 3

【新建列】

当年的第几周 = WEEKNUM(''[时间日期],2)

-- 第二个参数和WEEKDAY类似,用于规定哪天算一周的开始

3.平移指定月份

EDATE(日期,平移月数)

-- 返回按指定月份平移后的日期;正数向后,负数向前

EOMONTH(日期,平移月数)

-- 返回按指定月数平移后的月份的最后一天

举例:

EOMONTH(TODAY(),-1)    -- 代表今天的日期向前推一个月的月末日期

【新建列】

平移月份 = EDATE(''[时间日期],2)

平移月份月末日期 = EOMONTH(''[时间日期],2)

4.DATEDIFF间隔日期时间

DATEDIFF(起始日期, 结束日期, 间隔单位)

【新建列】

= DATEDIFF('2'[起始日期],'2'[结束日期],YEAR)

-- Tips: 如果计算保质期要+1,因为生产日期当天就算第一天了

5.YEARFRAC函数

-- 也是用来计算时间间隔的,比DATEDIFF更精确,比如可用于计算工龄

YEARFRAC(起始日期, 结束日期, <计算标准>)

【新建列】

= YEARFRAC('2'[起始日期],TODAY(),0)

-- 自行设置小数点位数

第二个参数:

-- 0

-- 1

-- 2

-- 3

-- 4

二十. 文本函数

1.CONCATENATE【将两个字符串连接】

语法:

CONCATENATE(<文本1 或 数字1>, <文本2 或 数字2>)

【新建列】

= CONCATENATE('CONCATENATE'[文本1], 'CONCATENATE'[文本2])

= CONCATENATE('CONCATENATE'[数字1], 'CONCATENATE'[数字2])

2.EXACT【判断字符是否相同】

语法:

EXACT(<文本1 或 数字1>, <文本2 或 数字2>)

【新建列】

= EXACT('CONCATENATE'[文本1], 'CONCATENATE'[文本2])

= EXACT('CONCATENATE'[数字1], 'CONCATENATE'[数字2])

应用场景举例:

配合IF语句,如果这两列内容一样返回什么,否则返回什么

3.FIND【找字符串在另一个字符串的起始位置】

返回一个文本字符串在另一个文本字符串中的起始位置;区分大小写;不支持通配符

语法:

FIND(<待查找内容>, <查找范围>, [<起始位置>], [<备选结果>])

[<起始位置>]:开始搜索的位置;如果省略,则起始位置为1,即从第一个字符开始找

[<备选结果>]:未找到文本时返回的值,可设为0、-1或BILANK();如果省略,则找不到时返回错误

【新建列】

= FIND('find'[子串], 'find'[字符串])

-- 将excel子串列的VBA改成小写,保存并刷新数据(PowerBI会重新加载数据),表达式报错

-- 传入<起始位置>和<备选结果>

= FIND('find'[子串], 'find'[字符串], 6, BLANK())

4.SEARCH【找字符串在另一个字符串的起始位置;不区分大小写;可用通配符

语法:

SEARCH(<待查找内容>, <查找范围>, [<起始位置>], [<备选结果>])

-- 语法和FIND相同

通配符:?代表一个字符,*代表多个字符

-- 不区分大小写

= SEARCH('find'[子串], 'find'[字符串])

-- 使用通配符

= SEARCH("孙?华", 'find'[字符串])

5.截取函数

语法:

LEFT([字段名], 取几个字符)    -- 从左向右取

RIGHT([字段名], 取几个字符)    -- 从右向左取

MID([字段名], 从第几个取, 取几个)    -- 从中间开始取

LEN([字段名])    -- 长度

【新建列】

= LEFT([字符串], 3)

= RIGHT([字符串], 3)

= MID([字符串], 3, 3)

= LEN([字符串])

6.FIXED【数值转字符;按指定小数位四舍五入】

将数值舍入到指定的小数位数,并将结果返回为文本;可以指定返回的结果是否包含千分符

语法:

FIXED(<数字>, [<小数位数>], [<逻辑值>])

[<逻辑值>]:如果为1,则不在返回的文本中显示千分符;如果为0或省略,则在返回的文本中显示千分符

【新建列】

= FIXED([数值], 3)

= FIXED([数值], 3, 1)

7.大小写转换

语法:

LOWER(<文本>)    -- 将文本字符串中的所有字母都转换成小写

UPPER(<文本>)    -- 将文本字符串中的所有字母都转换成大写

8.删除空格

删除文本前后的所有空格和单词之间多于一个的空格,保留单词之间的单个空格

语法:

TRIM(<文本>)

【新建列】

= TRIM([字符串])

如果要删除特定的空格,可以搭配截取函数LEFT()、RIGHT()、MID()并配合LEN()函数

9.重复字符串

语法:

REPT(<文本>, <重复次数>)    -- 按给定的次数重复文本

如果重复次数为0,REPT()返回空白;REPT()函数的结果不能超过32767个字符,否则将返回错误。

【新建列】

= REPT([字符串], 2)

= REPT([字符串], 0)

二十一. 替换函数

1.REPLACE【按指定位置替换字符串】

将指定位置的字符串转换为新的字符串

语法:

REPLACE(<文本>, <起始位置>, <替换长度>, <新内容>)

如果<替换长度>为空,或者引用包含空值的列,则新内容字符串插入到起始位置,不替换任何字符。

【新建列】

= REPLACE('REPLACE'[字符串], 3, 3, "孙兴华")

2.SUBSTITUTE【按指定内容替换字符串】

将指定的字符串替换为新的字符串;区分大小写

语法:

SUBSTITUTE(<文本>, <被替换内容>, <新内容>, [<匹配项>])

[<匹配项>]:代表替换第几个<被替换内容>,如果省略,则会替换所有找到的<被替换内容>

【新建列】

= SUBSTITUTE('SUBSTITUTE'[字符串], "孙兴华", "小孙")

二十二. 三角、数学、信息函数【不常用】

找一下老师的笔记

MOD【取余数】

返回指定数字被整除后的余数

语法:

MOD(<被除数>, <除数>)

二十三. 分组与连接函数

1.SUMMARIZECOLUMNS

是一种更灵活、更高效的SUMMARIZE实现方式。在编紫萼查询的时候,可以优先考虑SUMMARIZECOLUMNS。

语法:

SUMMARIZECOLUMNS(<groupBy_columnName1>, [<groupBy_columnName2>], …, [<filterTable>], …, [<name>, <expression>], …)

-- GroupBy_columnName参数描述里的可重复代表可以有多个分组依据

案例一:返回不重复姓名

原表:

【新建表】

不重复姓名 = SUMMARIZECOLUMNS('重复姓名'[姓名])

案例二:返回多列不重复

原表:

【新建表】

多列不重复 = SUMMARIZECOLUMNS('多列重复'[年份], '多列重复'[姓名])

注:可以不是同一张表,但是必须有关系的多张表。

案例三:返回汇总表【分组求和;使用第134参数】

【新建表】

汇总表 = SUMMARIZECOLUMNS('分组求和'[年份], '分组求和'[姓名], "总分", SUM('分组求和'[成绩]))

案例四:返回带筛选功能的汇总表【第二参数】

汇总表筛选 = SUMMARIZECOLUMNS('分组求和'[年份], '分组求和'[姓名], FILTER('分组求和', '分组求和'[科目] = "数学"), "数学", SUM('分组求和'[成绩]))

-- 相当于把原表筛成数学后对其进行

注意:

SUMMARIZECOLUMNS只适合新建表,不能在度量值中使用。

如果需要在度量值中执行分组和新建列时,最可靠的方式是SUMMARIZE+ADDCOLUMNS

2.ADDMISSINGITEMS【就是一个开关】

语法:

ADDMISSINGITEMS([<展示列>, …], <汇总列>, [<分组列>, …], <筛选条件>)

展示列:需要展示出来的列;可缺省;可多个

汇总列:经过筛选处理之后的表

分组列:用来分组的列;可缺省;可多个

例如:

SUMMARIZE不显示"成绩"为空白的人员

-- 原表

-- 汇总表(不显示孙兴华)

ADDMISSINGITEMS+SUMMARIZE可以显示(这个组合与单独使用SUMMARIZE出来的效果是一样的)

【新建表】

汇总表2 = ADDMISSINGITEMS('分组求和'[年份], '分组求和'[姓名], SUMMARIZECOLUMNS('分组求和'[年份], '分组求和'[姓名], "总分", SUM('分组求和'[成绩])), '分组求和'[年份], '分组求和'[姓名])

第一参数:和SUMMARIZECLOLUMNS相同的分组列

第二参数:SUMMARIZECLOLUMNS表达式

第三参数:和SUMMARIZECLOLUMNS相同的分组列

套ADDMISSINGCOLUMNS开关的场景:想显示空值记录的时候

3.INTERSECT、EXCEPT、UNION

INTERSECT(A, B) - 交集

EXCEPT(A, B) - 差集

UNION(A, B) - 并集

【新建表】

刺杀且死亡名单 = INTERSECT('刺杀名单', '死亡名单')

刺杀未死亡名单 = EXCEPT('刺杀名单', '死亡名单')

刺杀名单和死亡名单并集 = UNION('刺杀名单', '死亡名单')

4.CROSSJOIN笛卡尔积

表1:

表2:

【新建表】

笛卡尔积 = CROSSJOIN('1', '2')

二十四. 查找匹配函数

1.CONTAINS【多条件查找】

CONTAINS为简单筛选提供了更好的性能,而CALCULATETABLE适用于复杂的筛选表达式。

【新建度量值】

是否购买过 = CONTAINS('', ''[日期], DATE(2020, 12, 2), ''[姓名], "李四")

-- CONTAINS返回的是布尔值

2.TREATAS【无关系情况下查找匹配】

TREATAS函数时无关系情况下执行查找匹配的最佳选择。

语法:

TREATAS(<table_expression>, <column>[,[column]…])

作用:把什么当做什么

解释:把一参当做二参的筛选器,通过一参筛选二参

注意:一参是唯一值的表

案例一:单列筛选(求各月份的销售目标)

【新建表】

日期表 = ADDCOLUMNS(

CALENDAR(DATE(2020,1,1),DATE(2021,12,31)),

"",YEAR([Date]),

"",MONTH([Date]),

"",WEEKNUM([Date]),

"年季度",YEAR([Date])&"Q"&ROUNDUP(MONTH([Date])/3,0),

"年月",YEAR([Date])*100+MONTH([Date]),

"年周",YEAR([Date])*100+WEEKNUM([Date]),

"星期几",WEEKDAY([Date])

)

"销售目标"表中没有详细日期,无法与"日期表"建立直接的关系。

现在想用日期表筛选销售目标表,比如用'日期表'.[月]筛选'销售目标'.[销售目标]。把日期表的[月]用作筛选条件(一参),而销售目标表的[月份]则是被一参筛选的列(二参)。

【新建度量值】

月度销售目标 = CALCULATE(SUM('销售目标'[销售目标]), TREATAS(VALUES('日期表'[]), '销售目标'[月份]))

矩阵表行上放日期表的[月],值上放[月销售目标]

案例二:多列筛选(求各年份月份的销售目标)

【新建度量值】

年月销售目标1 = CALCULATE(SUM('销售目标'[销售目标]), TREATAS(VALUES('日期表'[]), '销售目标'[年份]), TREATAS(VALUES('日期表'[]), '销售目标'[月份]))

矩阵表行上放日期表的[年],列上放日期表[月],值上放[年月销售目标1]

多条件筛选时,如果数据量比较大,考虑速度问题,可以使用SUMMARIZE进行优化

年月销售目标2 = CALCULATE(SUM('销售目标'[销售目标]), TREATAS(SUMMARIZE('日期表', '日期表'[], '日期表'[]), '销售目标'[年份], '销售目标'[月份]))

-- SUMMARIZE和SUMMARIZECOLUMNS的唯一区别就是,前者要先指明是哪个表,然后在指明分组的列,而后者直接指明是哪个表的哪个列;SUMMARAIZECOLUMNS不能写在度量值里,会报错

3.ROW函数【返回一个单行表,其中包含由DAX表达式指定的新列】

语法:

ROW(<Name>, <Expression>, [<Name>, <Expression>], […])

说人话:就是创建一张表

ROW("新列名", 创建这个新列用什么表达式)

案例:

【新建表】

测试表 = ROW("总销量", SUM('Row'[销售量]), "总金额", SUM('Row'[销售金额]))

4.综合案例分析:利用TREATAS函数自由切换坐标轴

(1)利用笛卡尔积做表

思路分析:

【新建表】

坐标轴 =

UNION(

    UNION(

        CROSSJOIN(ROW("坐标轴", "城市"), VALUES('案例'[城市])),

        CROSSJOIN(ROW("坐标轴", "产品"), VALUES('案例'[产品]))

    ),

    CROSSJOIN(ROW("坐标轴", "年份"), VALUES('案例'[年份]))

)

-- UNION一次只能连接2张表,所以要使用两次UNION

-- UNION用第一张表的列名作为合并表的列名,可以手工改一下列名(双击列名),更好理解

(2)第二个切片器

【新建表】

切换 = UNION(ROW("结果", "销量"), ROW("结果", "销售金额")

(3)把两个总度量值写出来

【新建度量值】

总销量 = SUM('案例'[销量])

总金额 = SUM('案例'[销售金额])

(4)分别写显示销量与显示销售金额的度量值

【新建度量值】

显示销量 = SWITCH(

    SELECTEDVALUE('坐标轴'[坐标轴]),

    "城市", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[城市])),

    "产品", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[产品])),

    "年份", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[年份]))

)

-- 当切片器单选的时候,如果选"城市",显示各城市销量;如果选"产品",显示各产品销量;如果选"年份",显示各年份销量

显示金额 = SWITCH(

    SELECTEDVALUE('坐标轴'[坐标轴]),

    "城市", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[城市])),

    "产品", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[产品])),

    "年份", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[年份]))

)

(5)最后把结果做出来

OK = SWITCH(SELECTEDVALUE('切换'[结果]), "销量", '案例'[显示销量], "销售金额", '案例'[显示金额])

切片器1字段上放'坐标轴'[坐标轴],切片器2字段上放'切换'[结果],簇状柱形图X轴放'坐标轴'[类别],Y轴放'案例'[OK]

5.VAR声明变量

接上节课案例:

显示金额和显示销售我们做了两个度量值,又做了一个OK的度量值。

用VAR一步搞定

【新建度量值】

度量值 =

VAR xiaoliang = SWITCH(

    SELECTEDVALUE('坐标轴'[坐标轴]),

    "城市", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[城市])),

    "产品", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[产品])),

    "年份", CALCULATE('案例'[总销量], TREATAS(VALUES('坐标轴'[类别]), '案例'[年份]))

)

VAR jine = SWITCH(

    SELECTEDVALUE('坐标轴'[坐标轴]),

    "城市", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[城市])),

    "产品", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[产品])),

    "年份", CALCULATE('案例'[总金额], TREATAS(VALUES('坐标轴'[类别]), '案例'[年份]))

)

RETURN SWITCH(SELECTEDVALUE('切换'[结果]), "销量", xiaoliang, "销售金额", jine)

簇状柱形图Y轴换成'案例'[度量值],效果和'案例'[OK]一致

除了可以定义度量值,还可以定义新建列:

【新建列】

评价 =

VAR zongfen = '案例2'[数学] + '案例2'[英语] + '案例2'[语文]

RETURN IF(zongfen >= 270, "优秀", "一般")

-- 评价三门科目总分

等同于:

评价 = IF('案例2'[数学] + '案例2'[英语] + '案例2'[语文] >= 270, "优秀", "一般")

【实战案例】模糊查找

【新建度量值】

查询类型 =

VAR a = VALUES('查询表'[类型])

VAR b = MAX('销售表'[类型])

RETURN

    IF(

        COUNTROWS(FILTER(a, SEARCH('查询表'[类型], b, 1, 0) > 0)) > 0,

        b,

        BLANK()

    )

-- MAX('销售表'[类型]):借助聚合函数返回[类型]的值,这里的作用单纯是为了将[类型]列进行聚合,否则无法在查询函数中使用,用MIN也可以

-- 通过IF语句进行判断,符合条件显示,否则显示为空;内部利用FILTER进行上下文传递,将原本没有联系的维度和事实表连接在一起

矩阵表值上放'查询表'[查询类型],行上放'销售表'[电影],切片器字段上放'查询表'[类型]

6.检查字符串是否被包含

FIND和SEARCH函数都可以查找指定字符串所在的位置,如果只需要检查字符串是否被包含,可以使用CONTAINSSTRING和CONTAINSSTRINGEXACT,它们只进行逻辑判断,计算效率更高。

(1) CONTAINSSTRING 支持通配符,不区分大小写

CONTAINSSTRING(<WITHINTEXT>, <FINDTEXT>)

列 = CONTAINSSTRING("跟着孙兴华学VBA", "孙?华")

如果一个文本字符串包含另一个文本字符串,则返回TRUE。

CONTAINSSTRING支持通配符,不区分大小写,可以执行模糊匹配,使用时注意它的参数顺序。

问号(?):问号匹配任何单个字符

星号(*):星号匹配任何字符序列

如果想找到的是问号或星号本身,请在字符前键入一个波浪号(~) --即转义字符

(2) CONTAINSSTRINGEXACT 不支持通配符,区分大小写

CONTAINSSTRING(<WITHINTEXT>, <FINDTEXT>)

列 = CONTAINSSTRING("跟着孙兴华学VBA", "孙兴华")

如果一个文本字符串包含另一个文本字符串,则返回TRUE。

CONTAINSSTRINGEXACT不支持通配符,区分大小写,使用时注意它的参数顺序。

二十五. 排名函数

1.RANKX排名

语法:

RANKX(<table>, <expression>, [<value>], [<order>], [<ties>])

第一参<table>:表,也可以是用函数生成的表

第二参<expression>:表达式,聚合表达式或写好的度量值

第三参<value>:值,可选,可以是个聚合表达式,也可以是直接的数值;当<value>省略时,用<expression>代替

第四参<order>:排序,ASC或DESC,升序或降序

第五参<ties>:排序方法,skip国际排序(下一名的排序等于之前所有排序的数量+1),dense中国式排序(只累加排序,不考虑数量)-- 看PowerQuery S01E11排名

【新建度量值】

总金额 = SUM('销售表'[销售金额])

排名 = RANKX(ALL('销售表'), '销售表'[总金额])

矩阵表行上放'销售表'[商品编码],值上放'销售表'[总金额]、'销售表'[排名]

两个问题:总计的排名列不应该显示、A007和A008的排名不应该显示

想要让总计不显示,通用方法:IF + HASONEVALUE

【新建度量值】

排名优化1 = IF(HASONEVALUE('销售表'[商品编码]), RANKX(ALL('销售表'), '销售表'[总金额]), BLANK())

矩阵表值上加上'销售表'[排名优化1]

去掉A007A008

AND(TRUE, TRUE)才返回TRUE

AND(TRUE, FALSE)就返回TRUE

【新建度量值】

排名优化2 = IF(AND(HASONEVALUE('销售表'[商品编码]), '销售表'[总金额] >0), RANKX(FILTER(ALL('销售表'), '销售表'[总金额] > 0), '销售表'[总金额], , DESC), BLANK())

矩阵表值上只放'销售表'[排名优化2]

ALL函数是绝对排名的用法,如果是相对排名,可以自行替换成ALLSELECTED

如果不输入第三参,第二参也是第三参,一旦输入第三参,结果显示的是第三参按照第二参排名的位置。

假如有一份普通中学学生成绩的分数,和一份重点中学成绩排名,向看看普通中学的学生在重点中学能排第几,这就是实际第三参的用途。

-- 第三参没有展开讲,需看官方文档

【进阶】RANKX进行明细级别的排名

【新建列】

单品销售排名 = RANKX(FILTER('案例2', '案例2'[品种] = EARLIER('案例2'[品种])), '案例2'[销售金额])

2.TOPN【返回满足条件的前N行记录】

RANKX适合计算明细的名次数据,TOPN则可以批量返回结果,从一张表中返回所有满足条件的前N行记录。

它的特点是返回的不是值,而是前N行的表。

语法:

TOPN(N, , [表达式], [顺序可选项])

N值:排名前N位

表:想要提取的表

[表达式]:用来排序的度量值或表达式

[顺序可选项]:排序方式,0/FALSE/DESC降序,1/TRUE/ASC升序

【新建表】

查询销售前2名的记录 = TOPN(2, 'TOPN', 'TOPN'[销售金额], DESC)

-- 求前2名的销售金额之和

2名的销售金额之和 = SUMX(TOPN(2, 'TOPN', 'TOPN'[销售金额], DESC), 'TOPN'[销售金额])

-- 因为新建列是行上下文,不具备筛选功能,可以建成度量值或套上CALCULATE,但是在此例中没有意义,所以就不套了

注意,当销售金额有重复值时,TOPN拿到的不一定是前两名,并列的也会算在里面占用名额。在实际分析中,通常不是配合SUMX使用,而是配合AVERAGEX使用计算平均值。

案例:

-- 计算销售量前3名共卖了多少

【新建度量值】

销售额 = SUM('TOPN'[销售金额])

【新建表】

测试表 = TOPN(3, ALL('TOPN'), 'TOPN'[销售额], DESC)

【新建度量值】

销售量前3名共卖了多少 = CALCULATE('TOPN'[销售额], TOPN(3, ALL('TOPN'), 'TOPN'[销售额], DESC)

卡片图字段放'TOPN'[销量表前3名共卖了多少]

-- 计算销售量前3名的店铺共卖了多少:找出销售额最高的3家店铺,然后把金额加在一起

【新建表】(把TOP3的店号取出来)

测试表2 = TOPN(3, ALL('TOPN'[店号]), 'TOPN'[销售额], DESC)

【新建度量值】(通过TOP3店号筛选'TOPN'表,从而计算TOP3店铺的总销售额)

销售量前3名的店铺共卖了多少 = CALCULATE('TOPN'[销售额], TOPN(3, ALL('TOPN'[店号]), 'TOPN'[销售额], DESC))

卡片图字段放'TOPN'[销售量前3名的店铺共卖了多少]

【新建度量值】

3名的店铺销售占比 = DIVIDE('TOPN'[销售量前3名的店铺共卖了多少], CALCULATE('TOPN'[销售额], ALL('TOPN'[店号])))

-- 这里ALL函数的参数也可以是'TOPN'表,效果是一样的,都是计算整张表的总销售额

卡片图字段放'TOPN'[前3名的店铺销售占比]

3.CONCATENATEX【将多个值连接到一起,以文本的形式输出】

语法:

CONCATENATEX(<Table>, <Expression>, [Delimiter], [OrderBy_Expression], [Order1], …)

第一参<Table>:用于表达式每行计值的表;第一参数必须是唯一值的表

第二参<Expression>:用于逐行计算的表达式

第三参[Delimiter]:连接表达式结果的连接符

第四参[OrderBy_Expression]:排序使用的表达式

第五参[Order1]:排序逻辑

案例一:

【新建度量值】

三部曲 = CONCATENATEX(VALUES('CONCATENATEX'[人物]), 'CONCATENATEX'[人物], "", 'CONCATENATEX'[人物], DESC)

矩阵表行上放'CONCATENATEX'[小说],值上放'CONCATENATEX'[三部曲]

-- 按人物姓名首字母降序排

案例二:对结果进行排序(查询每个人哪天买了东西)

【新建度量值】

度量值1 = CONCATENATEX(VALUES('流水'[购物日期]), FORMAT('流水'[购物日期], "yyyy/mm/dd"), "", '流水'[购物日期], ASC)

矩阵表行上放'花名册'[姓名],值上放'花名册'[度量值1]

等效表达式:

度量值1 = CONCATENATEX(RELATEDTABLE('流水'), FORMAT('流水'[购物日期], "yyyy/mm/dd"), "", '流水'[购物日期], ASC)

-- 花名册找流水表,一端找多端,用RELATEDTBALE引路人

案例三:CONCATENATEX综合案例

-- 原数据

Step1. 利用CONCATENATEX函数将多个值连接在一起

【新建度量值】

购买商品 = CONCATENATEX(VALUES('补充案例'[商品]), '补充案例'[商品], "")

Step2. 让总计栏显示为空(通用方法:IF + HASONEVALUE)

【修改度量值】

购买商品 = IF(HASONEVALUE('补充案例'[买家昵称]), CONCATENATEX(VALUES('补充案例'[商品]), '补充案例'[商品], ""))

矩阵表行上放'补充案例'[卖家昵称],值上放'补充案例'[购买商品]

Step3. 计算购买数量和金额

购买数量 = SUM('补充案例'[销售数量])

消费金额 = SUMX('补充案例', '补充案例'[单价] * '补充案例'[购买数量])

-- SUM函数的参数只能是物理列,不能是表达式;可以先在表中新建列计算乘积,再到度量值里SUM,但是数据量大的情况下耗费内存;最好的方法是直接在度量值里用SUMX,一步搞定计算表达式及求和

矩阵表值上加'补充案例'[购买数量]和'补充案例'[消费金额]

Step4. 计算客户等级

【新建度量值】

客户等级 = IF(HASONEVALUE('补充案例'[买家昵称]), SWITCH(

    TRUE(),

    '补充案例'[消费金额] >= 3000, "金牌客户",

    '补充案例'[消费金额] >= 1000, "银牌客户",

    "铜牌客户"

))

-- SWITCH函数第一参数为TRUE()的效果:返回条件判断列表中第一个为TRUE的结果

矩阵表值上加'补充案例'[客户等级]

二十六. 人工造表最终方案

ADDCOLUMNS 从指定的表开始添加列

SELECTCOLUMNS 从空表开始添加列

1.ADDCOLUMNS

语法:

ADDCOLUMNS(<>, <名称1>, <表达式1>, …) 返回具有DAX表达式指定的新列的表

【新建表】

日期表 = ADDCOLUMNS(

CALENDAR(DATE(2019,1,1),DATE(2021,12,31)),

"",YEAR([Date]),

"季度",ROUNDUP(MONTH([Date])/3, 0),

"",MONTH([Date]),

"",WEEKNUM([Date]),

"年季度",YEAR([Date])&"Q"&ROUNDUP(MONTH([Date])/3,0),

"年月",YEAR([Date])*100+MONTH([Date]),

"年周",YEAR([Date])*100+WEEKNUM([Date]),

"星期几",WEEKDAY([Date])

)

解析:

(1) CALENDAR(<开始日期>, <结束日期>) 创建一张开始日期和节数日期之间的日期表

(2) 季度

ROUNDUP(<数目>, <[NumberOfDigits]>) 从零开始,向上舍入数值

e.g.

ROUNDUP(1.88) 向上取整数,返回2

ROUNDUP(1.88, 1) 保留一位小数向上取,返回1.9

数学题:

1/3=0.3333,向上舍入保留0位小数点,就是1

2/3=0.6667,向上舍入保留0位小数点,就是1

4/3=1.3333,向上舍入保留0位小数点,就是2

案例:

向'表'添加两列,分别是各班级总分和平均分

【新建表】

2 = ADDCOLUMNS('', "总分", CALCULATE(SUM('数据'[分数])), "平均分", CALCULATE(AVERAGE('数据'[分数])))

-- 因为要筛选,所以要套CALCULATE,也可以写度量值

【修改表】筛选出平均分小于50的

2 = FILTER(ADDCOLUMNS('', "总分", CALCULATE(SUM('数据'[分数])), "平均分", CALCULATE(AVERAGE('数据'[分数]))), [平均分] < 50)

2.SELECTCOLUMNS

语法:

SELECTCOLUMNS(<>, <名称1>, <表达式1>, …) 返回具有从表中选择的列以及DAX表达式指定的新列的表

第1参<表>:从中选择列的表

第2参<名称>:要添加的新列的名称,可重复

第3参<表达式>:要添加的新列的表达式,可重复

案例:

【新建表】

3 = SELECTCOLUMNS('销售表',

    "销售日期", '销售表'[日期],

    "商品名称", RELATED('商品表'[名称]),

    "销售数量", '销售表'[销售量],

    "销售金额", '销售表'[销售量] * RELATED('商品表'[进价])

)

-- RELATED:销售表找商品表,多端找一端,使用RELATED引路人

二十七. 日期时间函数【智能函数】

27.1 计算累计值

计算累计有2类常用的时间智能函数:

DATESYTD为代表的返回日期值的表函数、以TOTALYTD为代表的返回标量值的函数

YTD:计算本年的年初至今

QTD:季初至今

MTD:月初至今

1.DATESYTD类【已淘汰】

语法:

DATESYTD(<Dates>, <[YearEndDate]>) 返回此年度中截至当前日期的一组日期

第1参数:日期格式的列或返回单列的表达式,通常使用日期表的日期列

第2参数:年截止日,日期字符串,忽略年

-- 以表中最大日期作为本年

原始表:

【新建表】

测试表 = DATESYTD(''[日期])

-- 原始表最大日期是2021年的,所以返回2021年的年初至今日期

测试表 = DATESQTD(''[日期])

测试表 = DATESMTD(''[日期])

总结:

返回的是一张表

(1) DATESYTD(年初至今)

根据日期数据决定,相当于把日期数据排序,找到最大的年,再求年至今。如果数据中最大的年是2021年,那就计算2021年;如果数据中最大的年是2030年,那就计算2023年

(2) DATESQTD(季初至今)

先找到最大年,再找最大月,确定最大月所在季度后,将这个时间表拿出来

(3) DATESMTD(月初至今)

先找到最大年,再找最大月,将这个时间表拿出来

案例:计算年初至今的销售量

【新建度量值】

总销售 = SUM(''[销售])

年初至今 = CALCULATE('测试表'[总销售], DATESYTD(''[日期]))

卡片图字段上放'表'[年初至今]

-- TOTALYTD类函数可以代替CALCULATE+DATESYTD这样的组合。

2.TOTALYTD类【推荐】

【修改度量值】

年初至今 = TOTALYTD('测试表'[总销售], ''[日期])

语法:

TOTALYTD(<表达式>, <日期列>, <[筛选器]>, <[截止日期]>)

第1参<表达式>:返回标量值的表达式

第2参<日期列>:包含日期的列

第3参<[筛选器]>:可选,应用于当前上下文的筛选器参数,可以使布尔表达式或表表达式;即先把表筛选完之后再去算YTD

第4参<[截止日期]>:带有日期的文本字符串,用于定义年末日期,默认值为12月31日

TOTALYTD:计算本年的年初至今

TOTALQTD:季初至今

TOTALMTD:月初至今

【新建度量值】

年初至今1 = TOTALYTD('测试表'[总销售], ''[日期], FILTER('', ''[销售] < 50))

年初至今2 = TOTALYTD('测试表'[总销售], ''[日期], FILTER('', ''[销售] < 50), "03/31")

-- 3.31定义为年末日期,则从4.1开始算作一年的开始

通常6月30日是财务年度的结束日期,每年的7月1日是财务年的开始日:

TOTALYTD([总销量], '表'[日期], "06-30")

27.2 同比与环比

同比率 = (本期 - 去年同期) / 去年同期 * 100%

环比率 = (本期 - 上期)  / 上期 * 100%

上期可以是上季度、上月、上周,甚至上一日

1.同比推荐:SAMEPERIODLASTYEAR【前一年】

语法:

SAMEPERIODLASTYEAR(<日期列>)

官方:返回当前筛选上下文中前一年的一组日期

说人话:在表中找到日期列最大的年、月、日,找到它的同比时间

原始表:

【新建表】测试

前一年 = SAMEPERIODLASTYEAR(''[日期])

-- 先确定好哪个是今年,然后再找到去年;时间区间相同,今年1.1~6.30对应去年1.1~6.30

【新建度量值】计算同比

总销售 = SUM(''[销售])

同比销售金额 = CALCULATE([总销售], SAMEPERIODLASTYEAR(''[日期]))

卡片图字段上放'表'[同比销售金额]

-- 结果:1+0+2+3+4+5 = 15

2.环比:DATEADD

语法:

DATEADD(<日期列>, <偏移量>, <偏移单位>)

第1参<日期列>:包含日期的列

第2参<偏移量>:一个整数,从日期列中添加或减去的时间间隔数

第3参<偏移单位>:偏移量使用的单位,DAY/MONTH/QUARTER/YEAR

-- 返回的是一张表

原始表:

【新建表】测试

返回的是一张表 = DATEADD('DATEADD'[日期], 0, YEAR)

返回的是一张表 = DATEADD('DATEADD'[日期], 1, YEAR)

返回的是一张表 = DATEADD('DATEADD'[日期], -1, YEAR)

 

【新建度量值】测试

总销售 = SUM('DATEADD'[销售])

上年 = CALCULATE([总销售], DATEADD('DATEADD'[日期], -1, YEAR)

矩阵表行上放'DATEADD'[日期],值上放'DATEADD'[总销售]、'DATEADD'[上年]

案例:

原始表:

【新建度量值】

总销售 = SUM('2'[销售])

上年 = CALCULATE('2'[总销售], DATEADD('2'[日期], -1, YEAR))

上月 = CALCULATE('2'[总销售], DATEADD('2'[日期], -1, MONTH))

上季 = CALCULATE('2'[总销售], DATEADD('2'[日期], -1, QUARTER))

矩阵表行上放'表2'[日期],值上放'表2'[上年]、'表2'[上月]、'表2'[上季]

-- 7月的上季销售就是上季度第1个月的销售,即4月的销售;同理,8月的上季销售就是上季度第2个月的销售,即5月的销售

【新建度量值】算环比

本月销售 = TOTALMTD('2'[总销售], '2'[日期])

上月销售 = TOTALMTD('2'[总销售], DATEADD('2'[日期], -1, MONTH))

卡片图字段上放'表2'[本月销售],卡片图字段上放'表2'[上月销售]

和原始表一致:

3.利用时间计算累积值:PARALLELPERIOD

区别:

1PARALLELPERIOD函数返回的是完整的时间范围,而DATEADD函数返回的结果是可以间断的

2PARALLELPERIOD函数针对的是一段范围的数据汇总DATEADD函数通常用来计算同比环比问题,针对的是某一个点

语法:

PARALLELPERIOD(<日期列>, <偏移量>, <偏移单位>)

第1参<日期列>:包含日期的列

第2参<偏移量>:一个整数,从日期列中添加或减去的时间间隔数

第3参<偏移单位>:偏移量使用的单位,DAY/MONTH/QUARTER/YEAR

【新建度量值】测试

总销售 = SUM('2'[销售])

DATEADD = CALCULATE('2'[总销售], DATEADD('2'[日期], -1, QUARTER))

PARALLELPERIOD = CALCULATE('2'[总销售], PARALLELPERIOD('2'[日期], -1, QUARTER))

矩阵表行上放'表2'[日期],值上放'表2'[DATEADD]、'表2'[PARALLELPERIOD]

-- '表2'[PARALLELPERIOD]返回的是上个季度销售的总和

案例:

【新建度量值】

本季销售 = CALCULATE('2'[总销售], PARALLELPERIOD('2'[日期], 0, QUARTER))

上季销售 = CALCULATE('2'[总销售], PARALLELPERIOD('2'[日期], -1, QUARTER))

切片器字段上放'表2'[日期];折线图X轴上放'表2'[日期],Y轴上放'表2'[本季销售]、'表2'[上季销售]

-- 一般用于图表中,而不是计算环比值

27.3 计算移动总计

1.DATESINPERIOD

语法:

DATESINPERIOD(<日期列>, <起始日期>, <偏移量>, <偏移单位>)

返回给定区间中的所有日期组成的单列形式的表。

第1参<日期列>:包含日期的列

第2参<起始日期>:日期表达式

第2参<偏移量>:一个整数,从日期列中添加或减去的时间间隔数

第3参<偏移单位>:偏移量使用的单位,DAY/MONTH/QUARTER/YEAR

案例1:统计数据表中最后一天倒推最近12个月的销售额

原始表:

【新建表】测试

测试表 = DATESINPERIOD('3'[日期], MAX('3'[日期]), -1, YEAR)

【新建度量值】

测试 = CALCULATE(SUM('3'[销售]), DATESINPERIOD('3'[日期], MAX('3'[日期]), -1, YEAR))

卡片图字段上放'表3'[测试]

案例2:求最近30天的移动平均

原始表:

【新建表】测试

测试表 = DATESINPERIOD('4'[日期], MAX('4'[日期]), -30, DAY)

【新建度量值】

移动平均 = CALCULATE(SUM('4'[销量]), DATESINPERIOD('4'[日期], MAX('4'[日期]), -30, DAY)) / 30

卡片图字段上放'表3'[移动平均]

案例3:从上月算起,过去的3个月的日期

【新建表】

= DATESINPERIOD('4'[日期], EOMONTH(MAX('4'[日期]), -1), -3, MONTH)

-- EOMONTH函数用于平移指定月份(笔记19.03)

补充拓展知识1LASTDATEMAX的区别

LASTDATE返回的是一个表,MAX返回的是一个值。

在这里,使用MAX和LASTDATE都可以,等效表达式:

测试表 = DATESINPERIOD('4'[日期], LASTDATE('4'[日期]), -30, DAY)

但在有的情况下,这两个函数是有区分的,因为它们的返回值类型不同:

CALCULATE([度量值], LASTDATE('某张表'[日期列]))

CALCULATE([度量值], FILTER('某张表', '某张表'[日期列] = MAX('某张表'[日期列])))

当用于CALCULATECALCULATETABLE筛选器时,习惯用LASTDATEFIRSTDATE而不是MAXMIN

补充拓展知识2:动态日期表

【新建表】

日期表 = ADDCOLUMNS(

CALENDAR(FIRSTDATE('4'[日期]),LASTDATE('4'[日期])),

"",YEAR([Date]),

"季度",ROUNDUP(MONTH([Date])/3, 0),

"",MONTH([Date]),

"",WEEKNUM([Date]),

"年季度",YEAR([Date])&"Q"&ROUNDUP(MONTH([Date])/3,0),

"年月",YEAR([Date])*100+MONTH([Date]),

"年周",YEAR([Date])*100+WEEKNUM([Date]),

"星期几",WEEKDAY([Date])

)

-- 使用FIRSTDATE和LASTDATE函数自动生成数据表的日期表,而无需手动填入日期

2.ENDOFMONTHSTARTOFMONTH

语法:

ENDOFMONTH(<日期列>) 返回当月最后一天

STARTOFMONTH(<日期列>) 返回当月第一天

季度:

ENDOFQUARTER(<日期列>) 返回当季度最后一天

STARTOFQUARTER(<日期列>) 返回当季度第一天

年:

ENDOFYEAR(<日期列>, <[年度截止日期]>)

返回当年最后一天;<[年度截止日期]>:包含日期的文本字符串,用于定义年末日期,默认值为1231

STARTOFYEAR(<日期列>, <[年度截止日期]>)

返回当年第一天;<[年度截止日期]>:包含日期的文本字符串,用于定义年末日期,默认值为1231

【新建度量值】

每月最后3天销量 = CALCULATE(SUM('4'[销量]), DATESINPERIOD('4'[日期], ENDOFMONTH('4'[日期]), -3, DAY))

矩阵表行上放'表4'[日期]层级的月份(都是2020年的),值上放'表4'[每月最后3天销量]

每月前3天销量 = CALCULATE(SUM('4'[销量]), DATESINPERIOD('4'[日期], STARTOFMONTH('4'[日期]), -3, DAY))

进阶:返回当前上下文中最后一个日期或第一个日期

【新建表】

ENDOFMONTH('表4'[日期])    返回当前上下文中最后一个日期,类似MAX或LASTDATE函数

STARTDOFMONTH('表4'[日期])    返回当前上下文中第一个日期,类似MIN或FIRSTDATE函数

3.DATESBETWEEN区间日期

语法:

DATEDBETWEEN(''[日期], 开始日期, 结束日期)    返回两个给定日期之间的日期

【新建表】测试

测试表 = DATESBETWEEN('4'[日期], DATE(2020, 1, 1), DATE(2020, 1, 2))

注意,如果开始日期大于结束日期,结果为空。

【新建度量值】

区间销量 = CALCULATE(SUM('4'[销量]), DATESBETWEEN('4'[日期], DATE(2020, 1, 1), DATE(2020, 1, 2)))

卡片图字段上放'表4'[区间销量]

27.4 NEXT系列函数

1.NEXT系列函数【下一个】

表函数:

NEXTDAY【下一天】、NEXTMONTH【下一月】、NEXTDAY【下一季】、NEXTDAY【下一年】

例如:

DATESBETWEEN('表'[日期], DATE(2020, 1, 1), DATE(2020, 3, 10))

返回2020/1/1至2020/3/10这个期间的日期,如果在最外层套上NEXTDAY,就得到了2020/3/11这个日期的表。

案例:NEXTDAY实现错位值

原始表:

【新建度量值】

NEXTDAY = CALCULATE(SUM(NEXT[销售金额]), NEXTDAY('NEXT'[日期]))

矩阵表行上放'NEXT'[日期],值上放'NEXT'[销售金额]、'NEXT'[NEXTDAY]

例如:

DATESBETWEEN('表'[日期], DATE(2020, 1, 1), DATE(2020, 3, 10))

返回2020/1/1至2020/3/10这个期间的日期,如果在最外层套上NEXTMONTH,就得到了2020/4/1至2020/4/30这个日期的表。

案例:NEXTMONTH实现下个月销售的总计

【新建度量值】

NEXTMONTH = CALCULATE(SUM(NEXT[销售金额]), NEXTMONTH('NEXT'[日期]))

矩阵表值上加'NEXT'[NEXTMONTH]

2.PREVIOUS系列函数【上一个】

表函数:

PREVIOUSDAY【上一天】

PREVIOUSMONTH【上一月】

PREVIOUSDAY【上一季】

PREVIOUSDAY【上一年】

27.5 OPENINGBALANCE系列【期初库存】

值函数:

OPENINGBALANCEMONTH函数

OPENINGBALANCEQUARTER函数

OPENINGBALANCEYEAR函数

用途:计算月/季度/年的期初库存

注意,所谓月初库存就是上月底最后一天的库存。

语法:

OPENINGBALANCE函数(<表达式>, <日期>, <[筛选器]>)

案例:

原始表:

【新建度量值】

月初库存 = OPENINGBALANCEMONTH(SUM('期初'[库存]), '期初'[日期])

矩阵表行上放'期初'[日期],值上放'期初'[月初库存]

和原始表一致:

二十八. 筛选器函数

“筛选”函数,其本身不属于表函数,也不属于值函数,仅作为CALCULATE函数的调节器使用。

我们之前接触的函数,要么是返回表,要么是返回值。

28.1 REMOVEFILTERS【不推荐】

REMOVEFILTERS:移除筛选

-- 和ALL函数功能一样,但是ALL函数可以用来返回一张表,也可以放到筛选器里;而REMOVEFILTER只能放到CALCULATE的筛选器里,它是“筛选”函数。

REMOVEFILTERS函数与ALL函数特性相同(【注意ALL函数特性】):

(1)ALL参数为表时,忽略所有的筛选条件,无论是该图表内还是外部切片器

(2)当ALL参数为列时,忽略该列筛选,其他图表字段或外部筛选对其产生作用

(3)ALL函数在引用列的时候必须与矩阵的行和列在同一张表

案例:

原始表:

【新建度量值】

总销售 = SUM('REMOVEFILTERS'[销售金额])

ALL = CALCULATE('REMOVEFILTERS'[总销售], ALL('REMOVEFILTERS'))

REMOVEFILTERS = CALCULATE('REMOVEFILTERS'[总销售], REMOVEFILTERS('REMOVEFILTERS'))

矩阵表行上放'REMOVEFILTERS'[日期],值上放'REMOVEFILTERS'[总销量]、'REMOVEFILTERS'[ALL]、'REOVEFILTERS'[REMOVEFILTERS]

度量值 = CALCULATE('REMOVEFILTERS'[总销售], REMOVEFILTERS('REMOVEFILTERS'[日期]))

推荐直接使用ALL函数

28.2 KEEPFILTERS追加筛选】

案例:

原始表:

【新建度量值】

总金额 = SUM('KEEPFILTERS'[销售金额])

A商品销售 = CALCULATE('KEEPFILTERS'[总金额], 'KEEPFILTERS'[商品] = "A")

矩阵表行上放'KEEPFILTERS'[日期],值上放'KEEPFILTERS'[总金额]、'KEEPFILTERS'[A商品销售]

矩阵表行上换成'KEEPFILTERS'[产品]

-- 'KEEPFILTERS'[A商品销售]显示异常,B和C行不应该显示值;原因是表达式并没有告诉图表可以用商品进行筛选(没懂)

解决方法:

(1)【修改度量值】

A商品销售 = CALCULATE('KEEPFILTERS'[总金额], 'KEEPFILTERS'[商品] = "A", VALUES('KEEPFILTERS'[商品]))

-- 实现用商品进行筛选

(2)【新建度量值】

A商品销售KEEP = CALCULATE('KEEPFILTERS'[总金额], KEEPFILTERS('KEEPFILTERS'[商品] = "A"))

解析:

总结:

KEEPFILTERS是表达式内部筛选条件与外部筛选(比如图表中的字段筛选)取交集,在这里就是[商品] = "A"和矩阵表行上放[商品](度量值自动根据行筛选)两个筛选取交集;

CALCULATE自带的筛选器是表达式内部筛选条件取交集,在这里就是[商品] = "A"和VALUES([商品])两个筛选条件取交集,不涉及外部

28.3 CROSSFILTERS【交叉筛选】

语法:

CROSSFILTER(多端固定列名, 一端固定列名, 方向)

-- 必须是固定列名,不能是表达式生成的

方向:

Oneway - 单向筛选(一端只能筛选多端,多端只能向一端索取数据)

Both - 双向筛选(两张表之间既可以筛选,也可以索取)

None - 无交叉筛选

功能一:改变筛选方向

-- 一端可以筛选多端,多端可以向一端索取数据

现在要实现多端筛选一端(通过销售表筛选出商品表中的商品数量):

【新建度量值】

商品数量 = COUNTROWS(RELATEDTABLE('商品表'))

矩阵表行上放'销售表'[商品编码],值上放'商品表'[商品数量]

-- 显示错误,商品表中每个商品应该只有1个

 

改变方向 = CALCULATE('商品表'[商品数量], CROSSFILTER('销售表'[商品编码], '商品表'[商品编码], Both))

-- CROSSFILTER函数的一参和二参是用来告诉表达式两张表是通过什么字段连接的

-- Both实现双向筛选,从而使销售表可以筛选商品表

矩阵值上加'商品表'[改变方向]

-- 此例没有实际意义,只是演示CROSSFILTER可以改变筛选方向

功能二:使用模型关系筛选时,数据量过大会导致模型运载变慢,此时可以使用CROSSFILTER函数进行优化

【新建度量值】

销售表数量 = COUNTROWS('销售表')

矩阵表行上放'销售表'[商品编码],值上放'商品表'[销售表数量]

【新建度量值】

改变连接方向后 = CALCULATE('商品表'[销售表数量], CROSSFILTER('销售表'[商品编码], '商品表'[商品编码], OneWay))

矩阵表值上加'销售表'[改变连接方向后]

-- 结果和直接计算是一致的,但是数据量大时,速度比直接计算快很多

注意事项:

1、如果模型关系是一对一的情况,使用ONEWAY和BOTH没区别

2、如果多端列和一端列位置反了,函数本身会自我修正

3、此函数只能在接受筛选器作为参数的函数中使用

目前涉及的有CALCULATE、CALCULATETABLE、TOTALYTD、TOTALQTD、TOTALMTD,其他没有讲

4、CROSSFILTER函数会覆盖任何现有筛选关系

5、如果两个参数没有任何连接关系,那么返回结果会报错

6、如果使用多个CROSSFILTER,最内层的会覆盖外面的

28.4 USERELATIONSHIP

用途:

1、用来激活指定的关系

2、适用于做关联分析

计算机生成了可选文字: USERELATIONSHIp(COlumnNamel, ColumnName2)  指 定 要 在 簋 DAX 表 达 鯊 中 甲 的 现 有 关 。 过 龕 名 个 作 为 卢 的 列 定 义  该 茨 亭 为 参 丨

案例一:

处理订单数量与收货数量

原始表:

计算机生成了可选文字: 订 单 日 期 以  2020 年 7, § , 曰  2020 年 冫 月 , 曰  2020 年 冫 , § , 曰  2020 年 冫 , § , 曰  2020 年 冫 月 , 曰  2020 年 冫 , § , 曰  2020 年 7, § 2 曰  收 货 日 期 商 品 名 称 以 售 星 -  2020 年 7, § 5 三  2020 年 7, § 8 三  2020 年 7, § 了 三  2020 年 7, § 5 曰  2020 年 7, § 9 曰  手 , 1  手 机  手 机  手 机  平 板

【新建表】动态日期表

日期表 = ADDCOLUMNS(

    CALENDAR(FIRSTDATE('某宝'[订单日期]), LASTDATE('某宝'[收货日期])),

    "", YEAR([Date]),

    "季度", QUARTER([Date]),

    "", MONTH([Date]),

    "", WEEKNUM([Date]),

    "年季度", YEAR([Date]) & "Q" & QUARTER([Date]),

    "年月", YEAR([Date]) * 100 + MONTH([Date]),

    "年周", YEAR([Date]) * 100 + WEEKNUM([Date]),

    "星期几", WEEKDAY([Date])

)

【模型视图创建连接】

'日期表'[Date]分别连'某宝'[订单日期]、'某宝'[收货日期]

计算机生成了可选文字: 日 “  鼕 宝  乏 月  二 0  订 E 期  , 品 名 称  耋 E 期

-- [订单日期]为主,是实线;

-- [收货日期]为辅,是虚线,为虚拟关系(不可用的关系)

【新建度量值】

下单数量 = SUM('某宝'[销售数量])

送达数量 = CALCULATE('某宝'[下单数量], USERELATIONSHIP('某宝'[收货日期], '日期表'[Date]))

-- 使用USERELATIONSHIP函数让'某宝'[收货日期]和'日期表'[Date]建立真的连接关系,这样就可以作为筛选条件,筛选出已经送达的日期

矩阵表行上放'日期表'[月],值上放'某宝'[下单数量]、'某宝'[送达数量]

计算机生成了可选文字: 月  总 计  下 数 呈 送 达 数

-- 14个已送达,11个未送达

筛选器[月]显示条件改为“不为空”-> 应用筛选器,可隐藏未送达的

计算机生成了可选文字: 不 力 空  选 型 0  高 級 笮  丘 亍 匾 力 以 下 内 春 項  不 力 巨  且 0  亡 辛 三 丘 器

计算机生成了可选文字: 月  下 邕 量 量

案例二:

-- 目的:创建两个维度(e.g.2个[名称])的切片器,使两者都可以对事实表('圣斗士')进行筛选且互不影响;图表左侧显示切片器1的结果,右侧显示切片器2的结果

-- 目标效果:

计算机生成了可选文字: 布 罗 迪  呵 , 迪 巴  1 艾 奥 里 亚  艾 俄 洛 斯  , 迪 斯 马 斯 克  左 项 目 右  河 魯 迪 巴 名 称 艾 俄 洛 斯  20 . 00 年 龄 14 . 00  工  名 称  阿 布 罗 迪  匚 迪 巴  艾 里 亞  艾 俄 洛 斯  . 1 迪 斯 马 斯 “

原始表:

计算机生成了可选文字: 名 称 以 年 龄 以 身 高  河 魯 逵 巴  戳 加  : 自 斯 马 斯 克  沙 i 〔  艾 俄 斯  河 布 罗 逵  2  重  囗  75  以 0  以 丆  以 2  以 5  58  70  70  以 4  以 5  以 3  75  72  - 生 日 囗  4 三 冫 了  4 三 959  4 三 9 以 ,  44 竄  44059  40 §  44 , 24  44 贬 4  4 嫠  44 5  4 三 842  4 三 858  4 三  . 血 型 以  AB  0  0  0

【新建表】

-- 为了在切片器中使用

名称1 = VALUES('圣斗士'[名称])

名称2 = VALUES('圣斗士'[名称])

计算机生成了可选文字: 名 称 以  河 魯 逵 巴  戳 加  : 自 斯 马 斯 荒  艾 俄 斯  河 布 罗 逵

【模型视图创建连接】

-- 如果用[名称1]'名称'和'名称2'[名称]都分别直接去连接'圣斗士'[名称],那么2个切片器的筛选结果会互相影响

计算机生成了可选文字: 同 名 称 1  名 称  圣 斗 士  名 称  , 重  同 名 称 2  名 称

将'名称2'和'圣斗士'之间的连线改为虚线(使关系不可用):右击连线 -> 属性 -> 取消勾选“使此关系可用” -> 确定

计算机生成了可选文字: 此 亭 可 用  设 引 用 亮 整 性  交 叉 篪 选 器 方 向  在 两 个 方 向 上 用 妄 全 篪 选 器

计算机生成了可选文字: 同 名 称 1  名 称  圣 斗 士  名 称  , 重  同 名 称 2  名 称

【新建度量值】

总年龄A = SUM('圣斗士'[年龄])

总年龄B = CALCULATE('圣斗士'[总年龄A], ALL('名称1'[名称]), USERELATIONSHIP('圣斗士'[名称], '名称2'[名称]))

-- ALL('名称1'[名称]):在使用'名称2'做筛选时,不希望'名称1'的筛选对计算产生影响

-- SUM仅是示例使用,在此例[年龄]字段上中不具备意义,在实际使用中多会使用,比如计算销售额累计等

切片器1字段上放'名称1'[名称];矩阵表行上放'圣斗士'[名称],值上放'圣斗士'[总年龄A]、'圣斗士'[总年龄B];切片器2字段上放'名称2'[名称]

计算机生成了可选文字: 申 昌  囗 阿 布 逵  囗 阿 魯 逵 巴  囗 艾 俄 刍 斯  囗 迪 斯 马 斯 克  囗 卡 妙  囗 米 以  囗 養  囗 撒  囗 沙 加  囗 修 以  名 称 , 乞 年 齡  艾 舅 圭 江  艾 , 斯  总 计  , 乞 年 齡 B  囗 阿 布 逵  囗 阿 魯 逵 巴  囗 艾 三 圭 亚  艾 俄 刍 斯  囗 迪 斯 马 斯 克  囗 卡 妙  囗 米 以  囗 養  囗 撒  囗 沙 加  囗 修 以

-- 虽然能实现独立筛选,但不是目标效果

【新建表】这里手工建了

主页- > 输入数据 -> 输入行列信息 -> 加载

计算机生成了可选文字: 创 建 表  顶 目  名 称

计算机生成了可选文字: 号 以 [  , 名 称

【新建度量值】

左 =

VAR A = SELECTEDVALUE(''[序号])

VAR B =

    SWITCH(

        TRUE(),

        A = 1, SELECTEDVALUE('名称1'[名称]),

        A = 2, '圣斗'[总年龄A]

    )

RETURN

    B

右 =

VAR A = SELECTEDVALUE(''[序号])

VAR B =

    SWITCH(

        TRUE(),

        A = 1, SELECTEDVALUE('名称2'[名称]),

        A = 2, '圣斗士'[总年龄B]

    )

RETURN

    B

-- 通用模板,以后遇到文本显示用SELECTEDVALUE,遇到数值计算用度量值或SUM等,添加其他字段显示就换行继续A = 3…

将报表视图中的矩阵表换成表,可以调整字段位置;表的列上先后依次放'圣斗士'[左]、'表'[项目]、'圣斗士'[右];最终实现效果:

计算机生成了可选文字: 申 昌  名 称  囗 阿 布  囗 阿 魯 逵 巴  囗 艾 俄 刍 斯  囗 迪 斯 马 斯 克  囗 卡 妙  囗 米 以  囗 養  囗 撒  囗 沙 加  囗 修 以  顶 右  艾 舅 圭 名 称 艾  20m 齡  14 . 00  名 称  囗 阿 布  囗 阿 魯 逵 巴  囗 艾 三 圭 亚  艾 俄 刍 斯  囗 迪 斯 马 斯 克  囗 卡 妙  囗 米 以  囗 養  囗 撒  囗 沙 加  囗 修 以

二十九. 中文排序

案例1

原表:

计算机生成了可选文字: 季 节

报表视图矩阵表行上放[季节],值上放[销量]

计算机生成了可选文字: 季 节 销 量  春  荩  秋  夏  总 计 1  10  80  50  30

-- 季节不是按照春夏秋冬排序的,而是按照拼音首字母

右键表 -> 编辑查询 -> 添加列 -> 索引列 -> 从1 -> 关闭并应用

计算机生成了可选文字: , 〕 2 ]  . 以 量

表格视图选中季节列 -> 列工具 -> 按列排序 -> 索引

计算机生成了可选文字: 文 件  0 名 称  主 页  亨 巧  助  表 工 R  列 工 輿  不 ; 匚 总  , 聹  日 嵋 爿 千 分 戔  季 节  , 文  季 节

回到报表视图

计算机生成了可选文字: 季 节 销 量  春  夏  秋  总 计 1  10  30  50  80

-- 已按照春夏秋冬排序

案例2

原表:

-- 不是按照目标顺序排好序的

计算机生成了可选文字:

报表视图矩阵表行上放'表2'[月份],值上放'表2'[销量]

计算机生成了可选文字: 月 份 销 量  六 月  三 月  四 月  石 月  一 月  总 计  5  1 1  14  18  52

主页 -> 输入数据 -> 根据排序要求创建月份表

计算机生成了可选文字: 创 建 表  目 份

查看模型视图

-- 已通过月份自动连线

计算机生成了可选文字: : 1

表格视图月份表选中月份列 -> 列工具 -> 按列排序 -> 索引

计算机生成了可选文字: 文 件  0 名 称  主 页  片 綸  助  表 工 以  列 工 輿  不 ; 匚 总  列 序  日 爿 千  索 引  , 文  巾 | : 以 豢 引 回

-- 因为两张表的月份存在连接关系,月份表按照索引排序,在表2也会生效

报表视图矩阵表行上换成'月份表'[月份]

计算机生成了可选文字: 月 份 肖  一 月  18  二 月  3  三 月  1 1  四 月  14  石 月  1  六 月  5  总 计  52

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐