2.2.1 Power BI与Excel
Power BI中的分析公式分为两大类:DAX(表格型)公式和MDX(多维分析)公式。相比之下,DAX公式更为简化,易于理解,因此,近年来DAX公式渐渐成为主流趋势,而且DAX公式可用于Analysis Services、Power BI和Excel中。有人戏称,学会了DAX,就等于同时会了三种工具,本书内容也是围绕DAX公式展开的。
DAX与Excel函数同属于分析语言,许多Excel函数在DAX环境下也是通用的。例如,LEFT、IF、SUM等函数。但是,DAX和Excel函数又存在明显的不同。以下将从三个角度比较它们的差异。
DAX度量(Measure):依存于内存的列计算。Excel公式的计算依据为“单元格”,但在DAX中,“单元格”是不存在的。这样做极大地节约了计算资源,度量只有在被使用时才会占用内存,也不占用硬盘空间。由于度量计算是基于列的计算,其计算引擎十分高效,几十万行数值的聚合计算在4GB内存的电脑中运行是很轻松的事情。同时,DAX还会优化压缩源文件数据,PBIX文件往往比原有的Excel/文本文件更小。而Excel中的单元格计算不仅消耗内存资源,而且需要硬盘空间,其计算效能远不如DAX,复杂的Excel工作簿在运行时还会导致文件崩溃,强制关闭应用程序。
表关联:DAX的另一个特点是允许表与表依据一定的规则进行关联,这样避免了使用Excel工作表的必要。众所周知,Excel的工作表是导致Excel性能问题的一个重要因素,因为如果在Excel工作表中使用VLOOKUP公式则会产生大量的数据冗余,资源消耗会随着表的复杂程度的增长呈几何级增长,而DAX的表关联很好地弥补了Excel在此方面的局限。
引用变量:在Excel公式中经常使用单元格引用,这种方式虽然很方便,但也有极大的隐患,一旦单元格位置发生变化,则可能导致公式错误。因为DAX度量是列计算,不存在引用单一单元格的情况,除非整列字段被删除,否则DAX公式不受表格字段变动的影响。DAX公式还可以相互嵌套使用,实现许多复杂的业务逻辑。DAX公式有“写一次,永久运行”的优点。
说了这么多DAX的好处,那么DAX就没有缺点吗?并不是,DAX不是编程语言,DAX不仅没有单元格,也没有像While、Loop语句的循环逻辑。因此,当遇到非使用单元格不可或计算复杂循环逻辑的场景时,仍然需要通过VBA、SQL等编程语言解决,这不属于商业智能的范畴。
2.2.2 度量与计算列
DAX的计算公式可以分为两大类:度量(Measure)与计算列(Calculated Columns),表2.2.1中列举了二者的主要区别。
表2.2.1
另外,由于数量庞大的计算列会拖慢模型的性能,因此,在既可以使用度量又可以使用计算列的情况下,一般优先使用度量。如果需要使用计算列,则必须清楚是什么原因不能使用度量替代。
注意:度量为列计算,而计算列为行计算,列计算与计算列不是同一个事物。计算列公式与Excel公式都是相通的,运算方式也与Excel中的表运算相同,如图2.2.1所示,“@”表示其为Excel表(Table)。
图2.2.1
2.2.3 行上下文与筛选上下文
什么是上下文?比如朋友说今晚吃鸡。如果此刻你们在餐厅,那么你会理解为他想吃鸡肉。如果此刻你们在玩手机,那么你会理解为他想玩《荒野生存》游戏。这就是对上下文的通俗比喻:根据当前不同语境下所指的不同事物。DAX中的上下文分为两种:行上下文(Row Context)和筛选上下文(Filter Context)。
1.行上下文(Row Context)
行上下文比较容易理解,即进行“当前”行的操作,图2.2.1所示的是一个行上下文的例子,公式中虽然没有标明具体的行,但Excel只对“当前”行进行求和运算。在本质上,Excel表与Power BI中的计算列的运算原理都是依据行上下文操作的。
2.筛选上下文(Filter Context)
筛选上下文是指所有作用于DAX度量的筛选。这里将其筛选逻辑分为三个筛选层次(见图2.2.2所示),更好地帮助读者理解。
(1)外部筛选:任何存在于可视化层级的上下文筛选,包括任何图表本身、视觉级、页面级和报表级筛选器。外部筛选通过外部可视化操作对度量进行筛选操作。外部筛选也被称为隐性筛选,筛选设置不依存于度量中。
(2)DAX筛选:DAX筛选指DAX公式内部自身的筛选设置。例如,CALCULATE函数中的FILTER参数就是典型的DAX筛选。通过FILTER定义的筛选条件,可覆盖外部筛选的结果。DAX筛选也被称为显性筛选,因为筛选条件直接依存于公式自身,后文有具体介绍。
图2.2.2
(3)关联筛选:通过表之间的关联关系进行查询传递,DAX中的USERELATIONSHIP语句就是一个很好的例子,关联方式会改变外部筛选和DAX筛选的结果。
相比Tableau,DAX中没有类似表计算和LOD这样的专门术语,但DAX专有的函数可以实现相同的功能。例如,LOD中的FIXED、INCLUDE和EXCLUDE函数功能与DAX中的ALL、ALLEXCEPT、ALLSELECTED函数的功能对应。DAX度量的核心能力之一在于通过筛选上下文转换,将查询范围缩小至满足筛选条件的子集表中,在其内完成指定的聚合计算。
2.2.4 DAX精华公式
此处对DAX公式进行延伸,为读者介绍几款功能强大又有趣的公式。以下公式大部分来自微软官网,读者还可通过关键字在网上查找更详细的解释。
1.CALCULATETABLE——CALCULATE的姐妹
公式: CALCULATETABLE(<表达式>,<筛选器1>,<筛选器2>,…)
解释: “众所周知,CALCULATE是DAX的核心函数,可将计算列和度量进行上下文转换。通过DAX筛选返回满足条件的子数据集,再进行聚合运算并返回结果度量。作为CALCULATE的姐妹公式,CALCULATETABLE的工作原理与CALCULATE相似,只是CALCULATETABLE返回的是表,而非单一的度量。
示例: =SUMX(CALCULATETABLE('InternetSales_USD','DateTime'[Calendar Year]=2006),[SalesAmount_USD])
在上述例子中,CALCUCLATETABLE公式返回了一张日期为2006年的'InternetSales_USD'表中的子表。
2.CROSSJOIN——DAX中的笛卡儿积的表
公式: CROSSJOIN(<表格>,<表格>[,<表格>]…)
解释: CROSSJOIN用于返回使用表格中所有字段的所有行的笛卡儿积的表。
示例 :CROSSJOIN(Colors,Stationery)
3.SUMMARIZE——强大的依组分类摘要表
公式: SUMMARIZE(<表格>,<分类字段>[,<分类字段>]…[,<命名列名>,<表达式>]…)
解释: SUMMARIZE用于对表进行依据字段信息进行分类聚合的计算分析,返回的结果为信息摘要表。
在上述例子中,SUMMARIZE函数依据【CalendarYear】和【ProductCategoryName】维度对字段【SalesAmount_USD】进行求和。最终返回摘要表。熟悉SQL语句的读者对此不会感到陌生,SUMMARIZE函数的作用相当于SQL语句的GROUP BY的作用。
4.SUMMARIZECOLUMNS——跨表的摘要表
公式: SUMMARIZECOLUMNS(< 分类字段 > [,< 分类字段 >] … ,[< 筛选器表格 >] …[,<命名列名 >,< 表达式 >] … )
解释: 与SUMMARIZE相似,SUMMARIZECOLUMNS也是对数据进行摘要,但是不同的地方在于,SUMMARIZECOLUMNS所依据的维度字段可以不局限于一张表。
示例: SUMMARIZECOLUMNS ('Sales Territory'[Category],'Customer'[Education],FILTER('Customer','Customer'[First Name]=“Alicia”))
在上述公式中,公式依据源自不同表中的字段的【Category】、【Education】维度对满足条件'Customer'【First Name】=“Alicia”的表子集进行摘要分析。
5.ADDCOLUMNS——为表添加字段列
公式: ADDCOLUMNS(<表格>,<命名列名>,<表达式>[,<命名列名>,<表达式>]…)
解释: 将表达式以计算列的形式添加到指定的表中。
在上述公式中,将字段列【Internet Sales】和【Reseller Sales】分别添加至表Product Category中。
6.LOOKUPVALUE——DAX中的VLOOKUP函数
公式: LOOKUPVALUE(<结果栏字段>,<匹配栏字段>,<匹配值>[,<匹配栏字段>,<匹配值>]…)
解释: 为满足由<匹配栏字段>和 <匹配值>指定的所有标准的行返回 <结果栏字段>中的值。需要注意的是,匹配栏字段可以是多个字段选项。
公式: LOOKUPVALUE(Product[SafetyStockLevel],[ProductName],"Mountain-400-W Silver,46")
在上述例子中,通过字段【ProductName】的值“Mountain-400-W Silver,46”匹配所对应的【Product】表中的【SafetyStockLevel】(安全库存)字段。