Power BI之DAX神功:第3卷第6回 笛卡儿积CROSSJOIN、GENERATE 和 GENERATEALL
《火力全开》中我只讲了一个笛卡尔积CROSSJOIN,因为他能解决99%的笛卡儿积问题。
一、GENERATE函数两张表的笛卡儿积
《DAX神功》第2卷第9回 我们做过这样一个案例,计算每个人的销售排名

【度量值】总销售 = sum( '表2'[销售])
【度量值】排名 = RANKX ( GENERATE ( ALL ( '表1'[名称] ), ALL ( '表2'[编码2] ) ), [总销售])

二、笛卡儿积并不是非用不可
我们也可以通过下面公式实现排名:
度量值 = RANKX(calculatetable(SUMMARIZE('表2','表2'[编码2],'表1'[名称]),all('表1'[名称])),[总销售])
原理:
【1】SUMMARIZE('表2','表2'[编码2],'表1'[名称]) 取表2[编码2]与表1[名称]列去重后的临时表
详见《DAX神功》第1卷第15回

【2】名次不是通过行标题筛选出来的,而是V过来的
详见《DAX神功》第2卷第5回至第9回 我们证明了名次并不筛选出来的是V过来的
我的行标题上放表1[名称],所以我必须要取消(删除)这个列的筛选功能
我要使用all('表1'[名称])取消(删除)指定列的筛选功能,就必须使用引擎,数值的引擎是Calculate,表的引擎是CalculateTable
CalculateTable(临时表,all('表1'[名称])) // 返回的这张表与临时表长像一样,只是对表1名称列删除了筛选功能的表。
【3】最后使用RankX函数
RankX(CalculateTable(临时表,all('表1'[名称])),[总销售])
PS:我这里只用了all('表1'[名称])一个调节器,你可以再加上all('表2'[编码2]),根据你自己的需求而定。总之一句话,RankX函数它行标题上的列不能有筛选功能。这些原理在《DAX神功》第2卷第5回至第9回证明过。
这样做的弊端就是速度问题,SUMMARIZE生成临时表,CalculateTable筛选临时表,现在是教学案例只有几行,如果有几十万行速度还是有区别的。这时建议使用笛卡儿积,一次完成
三、GENERATE与CROSSJOIN函数的区别


新建表1 = GENERATE ('表1',RELATEDTABLE ('表2'))

新建表2 = GENERATE ('表1','表2')

新建表3 = CROSSJOIN ('表1','表2')

新建表1 = GENERATE ('表1',RELATEDTABLE ('表2'))
新建表2 = GENERATE ('表1','表2')
新建表3 = CROSSJOIN ('表1','表2')
我们发现新建表2和新建表3结果是一样的,CROSSJOIN支持多张表笛卡儿积,GENERATE支持两张表笛卡儿积。

但是,我们观察新建表1,结果与新建表2和新建表3不同,其实当你使用CalculateTable时结果同新建表1是一样的
新建表4 = GENERATE ('表1',CALCULATETABLE( ('表2')))

Calculatetable和Calculate一样都是将行上下文转换成筛选上下文
RELATEDTABLE作用:一端找多端。一端和多端什么关系?多端可以被筛选,相当于将行上下文转换成筛选上下文。

新建表1或新建表4生成方式不只这一种:我们换一种思路
我们发现,新建表2中,编码1=编码2 就是新建表1或新建表4的样子

所以我们也可以写成:
新建表5 = filter(GENERATE ('表1','表2'),'表1'[编码1]='表2'[编码2])
// 同理我们使用CROSSJOIN函数结果相同

四、将笛卡儿积应用到数据沿袭优势明显


// 《DAX神功》第2卷第21回 数据沿袭我们讲过这个公式的原理,但是表不只两张又怎么办?

我们可以使用笛卡儿积代替Selectcolumns,使用CROSSJOIN的优势:可以一次将筛选器应用到多张表
// 至于CROSSJOIN中使用ALL、VALUES、DISTINCT的区别在这里的新建表中是看不到的。每个函数的区别我们在前面的课程中都有详细介绍。例如你配合Calculate使用时,all和其它两个函数就有区别了,VALUES和DISTINCT区别:是否满足实时参照完整性返回空行的问题。

五、显示空行的GENERATEALL函数
表2中日期不连续:没有2021/1/3和2021/1/4

新建日期表:

【新建表】根据表2在表1中拿到名称 = SUMMARIZE (RELATEDTABLE ('表2'),'表1'[名称])

【新建表】不重复日期 = VALUES('日期表'[Date])


【新建表】generate = generate(VALUES('日期表'[Date]),SUMMARIZE (RELATEDTABLE ('表2'),'表1'[名称]))
// 如果表2没有相对应的日期,笛卡儿积之后会被忽略该日期。(隐藏空行)
// 不能直接用表1中的名称列,因为表1中没有日期

【新建表】generateall = generateall(VALUES('日期表'[Date]),SUMMARIZE (RELATEDTABLE ('表2'),'表1'[名称]))
// generateall与generate相反,它显示空行


孙兴华讲PowerBI火力全开》PowerBI必学课程
https://www.bilibili.com/video/BV1qa4y1H7wp
《DAX神功》文字版合集:
https://www.bilibili.com/read/readlist/rl442274
《DAX神功》视频版合集:
https://www.bilibili.com/video/BV1YE411E7p3
PowerBI(DAX函数)、PowerQuery(M函数)、Python办公自动化、Python爬虫、Python数据分析、ExcelVBA、WordVBA、AccessVBA、MySQL等等