本帖最后由 XF 于 2021-2-22 14:45 编辑
初识迭代函数
广义的迭代是对反馈过程的重复,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
在 DAX 中,迭代的含义有所不同,迭代函数遍历整个表,为表的每一行执行相同的 DAX 表达式,然后根据不同的函数执行不同的后续操作。DAX 的迭代函数数量很多,主要有两类
- 以 X 结尾的所有聚合函数,比如 SUMX,AVERAGEX 等,它们迭代后对所有值进行相应的聚合运算
- FILTER、ADDCOLUMNS、SELECTCOLUMNS、RANKX 等其他函数
它们都可以创建行上下文。本章我们介绍 FILTER 和以 X 结尾的聚合函数。
理解 FILTER
FILTER 函数的作用很简单:它获取一个表并返回一个与原始表具有相同列的表,逐行应用筛选条件,最后返回满足筛选条件的行。
FILTER 语法
- FILTER ( <table>, <FilterExpression> )
复制代码
FILTER 迭代整张表<table>,为每一行计值布尔表达式<FilterExpression>,当计值结果为 TRUE,FILTER 返回当前行;否则,就跳过当前行。
从逻辑角度来看,FILTER 为表中的每个行计值条件表达式。但是,DAX 的内部优化逻辑可能会将这些计算的数量减少到条件表达式中包含的列的唯一值的数量。条件表达式的实际计算次数对应于 FILTER 操作的“粒度”(Granularity)。这个粒度决定了 FILTER 的性能,它是 DAX 优化的重要因素。
例如,下面的查询筛选出产品表中的 Fabrikam 品牌
- EVALUATE
- FILTER (
- Product,
- Product[Brand] = "Fabrikam"
- )
复制代码
只筛选 Fabrikam 品牌的产品
FILTER 嵌套
可以将对 FILTER 的调用嵌套在另一个 FILTER 函数中,因为任何返回表的表达式都可以用作 FILTER 的参数。最内层的 FILTER 首先执行。通常,嵌套两个筛选器与用 AND 函数包含逻辑条件的结果相同。换言之,以下查询产生相同的结果:
- FILTER ( <table>, AND ( <condition1>, < condition2> ) )
- FILTER ( FILTER ( <table>, < condition1> ), < condition2> )
复制代码
但是,如果表有许多行,并且两个条件具有不同的复杂性,则可能会观察到不同的性能。例如,考虑下面的查询,它返回的单价是单位成本的三倍以上的 Fabrikam 产品,如下图所示。
- EVALUATE
- FILTER (
- Product,
- AND (
- Product[Brand] = "Fabrikam",
- Product[Unit Price] > Product[Unit Cost] * 3
- )
- )
复制代码
查询筛选出了单价是单位成本的三倍以上的 Fabrikam 产品
此类查询会将这两个条件应用于产品表的所有行。如果两个条件中有一个更快、更有约束性,则可以在内层的 FILTER 函数里首先应用它。例如,以下查询将对单位价格和单位成本的筛选应用于最内层的 FILTER 函数,然后按品牌筛选满足价格条件的产品。
- EVALUATE
- FILTER (
- FILTER (
- Product,
- Product[Unit Price] > Product[Unit Cost] * 3
- ),
- Product[Brand] = "Fabrikam"
- )
复制代码
如果反转条件,那么也会反转它们的执行顺序。以下查询仅将价格筛选应用于 Fabrikam 品牌的产品:
- EVALUATE
- FILTER (
- FILTER (
- Product,
- Product[Brand] = "Fabrikam"
- ),
- Product[Unit Price] > Product[Unit Cost] * 3
- )
复制代码
当你优化 DAX 表达式时,这些知识会很有用。你可以选择执行顺序,首先应用最具约束性的筛选器。然而,请在彻底清楚计值上下文之后再开始优化 DAX 语言。你将在 DAX 优化章节中找到关于查询优化的更完整的介绍。这些示例的目的是让你了解表函数在嵌套调用时的执行顺序。
随着 DAX 版本的更新,引擎的自动优化能力也在不断增强,在普通场景中,以上不同写法的实际性能差异已经微乎其微。但这种优化思路仍然有其参考价值,因为在更加复杂的模型和公式中,你无法完全依赖于引擎的自动优化。
关于执行顺序
通常,嵌套函数的执行顺序是从最内层函数到最外层函数。稍后你将看到,CALCULATE 和 CALCULATETABLE 可能是此行为的一个例外,因为用于计算它们的参数有特定顺序。考虑到你可能在某些相似情况下使用 FILTER 和 CALCULATETABLE,所以在嵌套调用时要注意这个区别。
测试题
你已经了解在 FILTER 中执行多个条件判断时的最优策略,让我们来回顾一下需要同时满足多条件(AND 运算)时,FILTER 所有可能的写法。以两个条件为例,使用 Contoso 样例数据库文件,定义以下三个公式:
- CALCULATE (
- COUNTROWS ( Sales ),
- FILTER (
- ALL ( Sales ),
- Sales[Order Date] <= DATE ( 2007, 1, 1 )
- && Sales[Delivery Date] >= DATE ( 2006, 1, 1 )
- )
- )
复制代码- CALCULATE (
- COUNTROWS ( Sales ),
- FILTER (
- FILTER ( ALL ( Sales ), Sales[Order Date] <= DATE ( 2007, 1, 1 ) ),
- Sales[Delivery Date] >= DATE ( 2006, 1, 1 )
- )
- )
复制代码- CALCULATE (
- COUNTROWS ( Sales ),
- FILTER ( ALL ( Sales ),[Order Date] <= DATE ( 2007, 1, 1 ) ),
- FILTER ( ALL ( Sales ),[Delivery Date] >= DATE ( 2006, 1, 1 ) )
- )
复制代码
它们的用时排序是怎么样的?
参考阅读:CALCULATETABLE vs FILTER
|