从数据到信息
从信息到洞察

理解FIRSTDATE 和 LASTDATE

在介绍半累加度量的时候,你接触了两个看似相近但行为非常不同的函数:LASTDATELASTNONBLANK。与它们相对应的还有两个函数用来获取第一个日期,而不是区间内的最后一个日期:FIRSTDATEFIRSTNONBLANK。本文描述 FIRSTDATELASTDATE 的行为,下一篇文章解释 FIRSTNONBLANKLASTNONBLANK

FIRSTDATE

FIRSTDATE ( <Dates> )

FIRSTDATELASTDATE 只操作日期列,它们分别返回活动筛选上下文中的第一个和最后一个日期,忽略其他相关表中存在的数据。其中<Dates>参数是单列日期类型的表,等价于:

CALCULATETABLE ( DISTINCT ( <Dates> ) )

FIRSTDATE 返回参数列在当前筛选上下文中的最小值,而 LASTDATE 返回的是最大值(注意,日期在内部表示为浮点数)。

用作 CALCULATE 筛选器的表函数

FIRSTDATE 和 LASTDATE 返回标量值,但同时也可以作为 CALCULATE 函数的筛选器参数,因为本质上它们返回的是单行表

LASTDATE 与 MAX 的区别

FIRSTDATE/LASTDATE 和 MIN/MAX 函数的语义区别在于前者返回一个表并执行上下文转换,而后者返回一个标量值不执行任何上下文转换。这样做的原因是,当你将这些表达式放入 CALCULATE 函数的筛选器参数中时,可以简化 DAX 语法。

例如,观察下面这个表达式:

CALCULATE (
    SUM ( Inventory[Quantity] ),
    LASTDATE ( 'Date'[Date] )
)

你可以用 MAX 代替 LASTDATE 将表达式改写成下面这种等效写法,但这会导致代码量增加:

CALCULATE (
    SUM ( Inventory[Quantity] ),
    FILTER (
        ALL ( 'Date'[Date] ),
        'Date'[Date]
            = MAX ( 'Date'[Date] )
    )
)

实际上,LASTDATE 函数还会执行上下文转换。因此使用原生 DAX 重写的准确表达式如下:

CALCULATE (
    SUM ( Inventory[Quantity] ),
    VAR LastDateInSelection =
        MAXX ( CALCULATETABLE ( DISTINCT ( 'Date'[Date] ) ), 'Date'[Date] )
    RETURN
        FILTER ( ALL ( 'Date'[Date] ), 'Date'[Date] = LastDateInSelection )
)

行上下文中执行 FIRSTDATE/LASTDATE 时,需要考虑行上下文转换。最佳做法是

  • 当定义筛选器参数表达式时(结果是表) ,使用 FIRSTDATE / LASTDATE
  • 在行上下文中定义逻辑表达式时(通常结果是一个标量值) ,使用 MIN/MAX 函数

因为 LASTDATE 执行了上下文转换,隐藏了外部筛选上下文

行上下文转换时的不同行为

当用作 CALCULATE/CALCULATETABLE 函数的筛选器参数时,我们更倾向于使用 FIRSTDATE/LASTDATE 而不是 MIN/MAX,因为前者的语法更简单(我们已经看到过几个这种用法的示例)。但是,当 FIRSTDATE/LASTDATE 隐含的上下文转换改变计值行为时,为了避免这种情况,你应该使用 MIN/MAX, 比如 FILTER 函数中的条件计算属于这种情况。下面的表达式筛选出了用于计算移动总计的日期

FILTER (
    ALL ( 'Date'[Date] ),
    'Date'[Date]
        <= MAX ( 'Date'[Date] )
)

使用 MAX 是正确的做法。事实上,如果你用 LASTDATE 而不是 MAX,你不会得到任何错误提示,但结果总是全部日期,忽略外部筛选上下文。因此,下面的表达总是错的:

FILTER (
    ALL ( 'Date'[Date] ),
    'Date'[Date]
        <= LASTDATE ( 'Date'[Date] )
)

我们使用以下两个版本的度量值计算移动总计筛选器返回的天数即可看出问题:

[RT Days MAX] :=
COUNTROWS (
    FILTER (
        ALL ( 'Date'[Date] ),
        'Date'[Date]
            <= MAX ( 'Date'[Date] )
    )
)

[RT Days LASTDATE] :=
COUNTROWS (
    FILTER (
        ALL ( 'Date'[Date] ),
        'Date'[Date]
            <= LASTDATE ( 'Date'[Date] )
    )
)

图 7-43 显示筛选出的移动总计天数对于 RT Days MAX 度量值是正确的,但是对于 RT Days LASTDATE 是错误的。原因是 LASTDATE 为每个日期执行上下文转换后总是返回当前行的值,最终导致 FILTER 返回所有日期。

图 7-43 RT Days LASTDATE 总是统计日期表的所有行

36
说点什么

1000
 
鼓掌微笑开心憧憬爱你色并不觉得吃瓜doge二哈喵喵思考笑哭捂脸悲伤大哭抓狂汗偷笑打脸捂眼黑线问号晕拜拜闭嘴衰咒骂ok作揖
10 评论数
31 被回复的评论
8 订阅评论的人数
 
查看最近回复
查看最热评论
  订阅本文评论  
最新 最旧 得票最多
提醒
成员
做一名学霸

老师,我是否可以归纳如下规律:
所以实际相当于带了隐式CALCULATETABLE或CALCULATE函数的其他函数,都会触发行上下文的转换,且如果转换后为筛选上下文后,会覆盖已存在的相同外部筛选上下文。

成员
139****3194

FILTER (
ALL ( ‘Date'[Date] ),
‘Date'[Date]
<= LASTDATE ( 'Date'[Date] )
)
老师,这里filter如果作为cal的内参,lastdate的参数能被初始筛选器或初始行上下文计值吧,如果能,这一步不是覆盖了filter 行上下文转换的日期了吗?最后和max累计一样效果吧?

成员
139****3194

CALCULATE (
SUM ( Inventory[Quantity] ),
VAR LastDateInSelection =
MAXX ( CALCULATETABLE ( DISTINCT ( ‘Date'[Date] ) ), ‘Date'[Date] )
RETURN
FILTER ( ALL ( ‘Date'[Date] ), ‘Date'[Date] = LastDateInSelection )
)
老师,上面calculateable的第二参数 ‘Date'[Date],也能被初始筛选上下文月份筛选?对吧?

成员
139****3194

[RT Days LASTDATE] :=
COUNTROWS (
FILTER (
ALL ( ‘Date'[Date] ),
‘Date'[Date]
<= LASTDATE ( 'Date'[Date] )
)
)
老师,filter的第二参行上下文转换为筛选上下文后覆盖外部月份日期筛选上下文,对吗?

成员
xifeng

老师,LASEDATE若发生行上下文转换,那么它的计值环境应该是采用行上下文转换的筛选上下文覆盖外部筛选上下文后得到的筛选上下文,那么它的等价写法应该要再套一个CALCULATETABLE吧

2020-08-31_113514.jpg
游客
xifeng

你好,关于本文倒数第二个度量值,其中的max函数的筛选环境是什么呢,是日期列的所有值,还是数据透视表行标签所筛选出来的值?
从结果来看的话应该是经过数据透视表筛选后的环境,但是filter中的all不是清除掉了筛选吗,请老师指点,感觉DAX的三观又碎了

游客
Lily

本文最后一个度量值,如果考虑透视表行标签被筛选了,那应该和max返回一样结果,为什么2556呢?请老师指点

成员
叶余

第一段的“LASDATE”应该是笔误了

DAX 圣经

导读

初识 DAX

DAX 基础知识

DAX 原理

DAX 高级原理

基础函数类型

迭代函数

CALCULATE 函数

CALCULATE 调节器

基础表函数

条件判断函数

查找匹配函数

时间智能函数

统计类函数

投影函数

分组/连接函数

集合函数

其他函数