跳至正文

DAX Guide-3-“百变”筛选FILTER与计算优先级

标签:

欢迎观看系列视频: UDEMY【另眼看DAX分析世界】 (该视频23年仅在UDEMY试发行)

筛选是问题分析的关键,也是问题中最为多变的部分,正因为此,性能优化的最主要空间也来自于筛选优先级的调整。

本文基于两个简单的案例,介绍筛选的两种最基本形式,并理解DAX中计算优先级的最基本设定。案例如下:

  • 2019年,各订单日期的 数量总和
  • 2019年,各订单日期的 数量总和,仅保留大于300的天

一、从数据表的角度理解筛选的位置

正如第一节所言,分析的本质是聚合,聚合是从明细表到问题或者说报表的过程。我们可以把这个过程形象的标记为如下的图示过程:

筛选就发生在这个过程之中,只是位置千变万化,甚至出现书写位置和计算位置不一致的情况,进一步增加了复杂性。

1、先看第一个案例:2019年,各订单日期的 数量总和。

这个过程,就是在聚合之前,或者说分组聚合之前,预先完成“筛选”,由于数据表中没有“年度”字段,筛选之前还要预先完成“订单年度”的预先计算。在SQL中,就是datepart函数,在DAX中就需要使用计算列(calcualted column)。

select 
  "Order Date", 
   sum(quantity)     -- 分组之后,组内聚合
from public.superstore_en   -- 查询明细表 
where  date_part('year',date("Order Date"))=2019   -- 其次,过滤不需要的明细行
group by "Order Date"   -- 再次分组  

DAX中,我们既可以使用SUMMARIZE+FILTER组合,也可以直接使用包含FILTER的SUMMARIZECOLUMN函数。不管是哪一种方式,都不像SQL一样易于理解背后的优先级次序。

如下所示,使用SUMMARIZE可以快速定位聚合的明细表、聚合的分组依据、聚合的度量。难点在于,为什么筛选条件跑到了聚合之中?

不管是哪个工具,这里的行级别计算、行级别筛选的逻辑都是普适性的。可以用下面的图示理解。

2、再看第二个案例:在上述基础上,增加聚合后的筛选条件

聚合后的筛选条件,筛选的对象就不在以明细表为基础,而是以聚合表为基础。这个过程可以形象的表示为如下过程。

在SQL中,可以使用having表示聚合后的筛选条件,如下所示:

select 
  "Order Date", 
   sum(quantity)     -- 分组之后,组内聚合
from public.superstore_en   -- 查询明细表 
where  date_part('year',date("Order Date"))=2019   -- 其次,过滤不需要的明细行
group by "Order Date"   -- 再次分组  
having  sum(quantity) >50

由于SQL有明确的优先级概念,所以我们很容易理解依次从from、where、group by分组聚合,最后到having的过程。

但在DAX中,筛选不在以字段的方式出现,而是回到了“从表到表”的过程,只是这次的起点变成了聚合表,重点也是聚合表。

相比前面的筛选,这里的筛选体现了更加清晰的优先级次序,即只有在聚合之后,方可完成基于聚合的筛选条件。

二、编程式分析语言和函数式分析语言的差异

在上面的案例中,我们可以看出SQL作为编程时语言的巨大优势:计算优先级异常清晰;同时也能感受到DAX作为函数式分析语言的卓越之处:函数简化了每个内部功能组合。

当然,对方的优点也正是自己的弱点。SQL的函数少,每个分析功能都要反复的组合;DAX缺乏优先级,单单是上述两种筛选的位置和次序,就已经让很多人开始困惑。而大师们不得不创造一些更加隐晦的概念(比如Context)进一步诠释组合的关系和优先级,这又让原本就不够清晰的优先级变得更加复杂。

在这里,大家先牢记一个基本原则:行级别的计算和判断条件,一定优先于聚合的计算和判断条件,就像COLUMN一定先于MEASUER一样。因为只有COLUMN做好了字段的准备,问题和聚合才成为可能。

在后续的进阶篇,我们将进一步介绍计算的优先级,这也需要建立在表查询、表函数的理解基础之上。

这里,可以提供一个思考题,为什么在上面的FILTER中,FILTER (sales,YEAR ( Sales[Order Date] ) = 2007 中的年度字段不再需要预先定义一个计算列就能完成?而在之前的案例中,作为维度字段的年度必须借助于define COLUMN呢?

有兴趣的可以留言回复。

Sep 3, 2023 V0.1


了解 喜乐君 的更多信息

订阅后即可通过电子邮件收到最新文章。