点击上方蓝字关注我们!
本文翻译自SQLBI
我们总是将日期表标记为日期表,因为我们知道那是最佳实践。但是为什么呢?如果你没有那样做,会发生什么呢?
为了理解标记为日期表的重要性,我们使用一个简单的YTD计算温习一下DAX的计算上下文:
Sales YTD =CALCULATE ([Sales Amount],DATESYTD ( 'Date'[Date] ))
该度量值依赖DATESYTD函数,该函数返回了1月1日至当前上下文中可见的最后一个日期之间的所有日期。DATESYTD函数达到的效果与下面的表达式达到的效果一致:
VAR LastVisibleDate = MAX ( 'Date'[Date] )RETURNFILTER (ALL ( 'Date'[Date] ),'Date'[Date] >= DATE ( YEAR ( LastVisibleDate ), 1, 1 )&& 'Date'[Date] <= LastVisibleDate)
因此,DATESYTD函数返回的一张含有多个日期的表,更具体一点,DATESYTD函数包含了列'Date'[Date]的某些值,所以当该度量值放到report中时,才会得到我们想看到的结果:

即使这样没有让你感到惊讶,实际上应该让你感到惊讶,上面提到的度量值不能正常工作。实际上它正常工作的原因是销售表与日期表之间的关系使用了列Sales[Order Date], 该列的数据格式是日期。如果我们将Sales[Order Date]换成数字格式的话,该度量值就会失效。现在我们改变关系的关联键,用数字列[OrderDateKey] 代替 日期格式列Sales[Order Date]

下面的图表展示了新关系下的结果,该度量值YTD不再正常工作

让我们探究一下原因。关注4月份这一行, Sales Amount和Sales YTD返回1,128,104.92。下面的表达式是Sales YTD进行计算的筛选上下文:
'Date'[Calendar Year] = "CY 2007",'Date'[Month] = "April"
因此,对Sales YTD的计算执行如下步骤:
CALCULATE ([Sales YTD],'Date'[Calendar Year] = "CY 2007",'Date'[Month] = "April")
假如你展开Sales YTD,它就会变成如下:
CALCULATE (CALCULATE ([Sales Amount],DATESYTD ( 'Date'[Date] )),'Date'[Calendar Year] = "CY 2007",'Date'[Month] = "April")
这时,Sales Amount的计算筛选上下文是什么?我们知道DATESYTD返回的是Date[Date]中的值。因此,DATESYTD产生的筛选器只筛选Date[Date]. 它不会覆盖外部的Date[Calendar Year]和
Date[Month]上的筛选器,因此计算Sales Amount的筛选上下文变成了:
DATESYTD ( 'Date'[Date] )'Date'[Calendar Year] = "CY 2007"'Date'[Month] = "April"
结果是,筛选器2007年4月比DATESYTD函数返回的过滤器更具有限制性,因为过滤器取的是交集,因此结果与没有应用DATESYTD筛选器的结果相同。
您可以看到,顺着计算步骤,我们得到了正确的结果:公式没有正常工作,因为它是错误的。为什么同样的公式适用于关系基于Date[Date]列的呢?
为了简化时间智能函数的用法,当两张表基于Date类型的列建立关系时,DAX引擎会执行以下假定:当一个筛选器应用于关系的键上时-在本例中是Date[Date]-新的筛选器会覆盖该列上的其他任何筛选器。每当在Date[Date]列上应用筛选器时,它会将REMOVEFILTERS (Date)应用到筛选器上下文。只有当关系基于Date数据类型的列时,才会自动发生此
行为。
通过将表标记为日期表,您可以获得相同的行为——即每当在Date列上应用新筛选器时,就在表上添加REMOVEFILTERS。当将表标记为日期表时,Power BI会询问哪一列包含日历的日期。这是必需的,因为每次在特
定列上应用筛选器时,引擎都会添加REMOVEFILTERS。

将Date表标记为日期表后,结果再次正确。值得注意的是,只有当您对Date[Date]列应用筛选时,才应用REMOVEFILTERS。如果将筛选器移动到任何其他列,则不会将REMOVEFILTERS应用于筛选器上下文。
例如,下面的度量值在Date[Calendar Year]列上添加了一个筛选器。该引擎只替换列年上的过滤器,并保持任何其他筛选器可用:
Sales 2008 =CALCULATE ([Sales Amount],'Date'[Calendar Year] = "CY 2008")

在上述的表中,我们假设计算CY 2007 4月份,那么度量值Sales 2008执行代码如下:
CALCULATE (CALCULATE ([Sales Amount],'Date'[Calendar Year] = "CY 2008"),'Date'[Calendar Year] = "CY 2007",'Date'[Month] = "April")
内部的筛选器2008覆盖了在列年上的筛选器,并没有覆盖月份上的筛选器,所以结果就是2008年4月份的结果。
到这里,你知道了当将一个日期表标记为日期表时发生了什么,现在可以得出结论:
1. 假如Sales表和日期表之间是基于日期列建立关系时,DAX引擎会自动的将REMOVEFILTERS应用于日期表,此时不需要将日期表标记为日期表,因为该行为会隐式发生。尽管如此,为了丰富模型元数据,以便客户端工具能够改善用户体验,继续这样做是一个好主意。
2. 如果Sales和Date之间的关系基于非Date数据类型的列,则必须将该表标记为日期表,以便通过时间智能计算获得预期的行为。
3. 如果不想将表标记为日期表,可以通过向使用时间智能函数的每个度量值添加REMOVEFILTERS (date)来获得相同的行为。尽管如此,添加所有这些REMOVEFILTERS还是很无聊的,因此将表标记为日期表是一种最佳实践。
4.你可以根据需要标记更多的日期表,DAX会对所有标记为日期表的数据表执行相同的处理
将Date表标记为日期表是最佳实践。如果你过去习惯这样做,你可以安全地继续这样做。现在您也知道为什么将表标记为日期表是好的。知道为什么总是比盲目遵循规则好。结果是相同的,但是这种意识对于排除错误的结果
是非常有用的。
参考:
我是BISeven,欢迎讨论,欢迎关注





