許多剛開始使用DAX的小伙伴在使用LASTDATE搜索某個時間段內的最后日期。或者他們使用NEXTDAY檢索給定日期之后的日期。盡管這些函數可以實現它們所承諾的功能,但它們並不打算在簡單的表達式中使用。相反,它們是設計用於時間智能計算的表函數。錯誤地使用它們會導致代碼效率低下。此外,以未設計的方式使用這些功能是一個明顯的信號,表明開發人員仍未掌握DAX的某些細節。
在本文中,我們將詳細介紹該主題,以便了解這些時間智能功能的作用。我們還想了解在日期上將它們與簡單的數學混淆如此容易的原因。我們想通過例子來詳細闡述這個話題。因此,我們從無聊的理論開始,而不是從一個計算開始,盡管這種計算工作得很好,但它本質上是錯誤的。
計算給定選擇中包含的天數,並生成如下所示的報告。
計算DaysInPeriod很簡單:天數是時間段中第一個日期和最后一個日期之間的差。DAX提供了兩個功能:FIRSTDATE和LASTDATE,這似乎是完美的候選者:
Days in period := INT ( LASTDATE ( 'Date'[Date] ) - FIRSTDATE ( 'Date'[Date] ) )
這項措施可以正常工作並產生正確的結果。因此,我們很高興!對?錯誤。我們不滿意,因為我們使用LASTDATE來檢索一個時間段內最后一個可見日期的值。LASTDATE完全執行此作業,但它返回一個包含最后日期的表-不僅是日期。讓我重復一遍:它不返回日期。它返回一個包含日期的表。
這樣做的原因是LASTDATE是時間智能功能。其主要目的是用作CALCULATE中的過濾器參數。CALCULATE過濾器參數是表。因此,要使函數在以下度量中使用,它需要返回一個表:
SalesOfLastDay = CALCULATE ( [Sales Amount], LASTDATE ( 'Date'[Date] ) )
您可以使用DAX Studio 再次檢查LASTDATE的結果。LASTDATE返回一個表。這就是為什么您可以在EVALUATE語句中使用它的原因,該語句需要一個表作為結果。
如您所見,結果是一個表,其中包含一列(Date)和最后一個日期的值。
在DAX中,可以使用僅包含一行和一列的表(即您從LASTDATE獲得的結果)代替內部的值。實際上,單行一列的表僅包含一個值。這就是DAX允許您將表自動轉換為值的原因。這也是您可以在我們的度量中減去兩個表的原因:
Days in period := INT ( LASTDATE ( 'Date'[Date] ) - FIRSTDATE ( 'Date'[Date] ) )
實際上,LASTDATE和FIRSTDATE都返回表。因為我們使用的是減法運算符,所以DAX會將兩個表轉換為標量值,然后計算表內包含的值之間的差。
盡管此行為是透明的,但它是有代價的。表示先前計算的一種更好的方法是使用標量函數,例如MIN而不是FIRSTDATE和MAX而不是LASTDATE。MIN和MAX不返回表:它們返回第一個和最后一個日期的值。因此,以下是對措施的更好表述:
Days in period MIN MAX := INT ( MAX ( 'Date'[Date] ) - MIN ( 'Date'[Date] ) )
同樣,您可以使用DAX Studio 再次檢查MIN和MAX的結果。如果您嘗試使用MAX而不是LASTDATE作為EVALUATE語句的結果,則會出現錯誤。
為了獲得EVALUATE的結果,您需要構建一個包含最大日期的表。例如,您可以使用表構造函數執行此操作。
如前所述,DAX自動將具有一行和一列的表轉換為值。但是這種行為是有代價的。此外,LASTDATE和FIRSTDATE都在查找第一個和最后一個日期之前執行上下文轉換。此行為不會影響我們的簡單示例,但是僅由於此方面,在更復雜的情況下性能可能會很差。
本文是DAX 101;因此,它應該在這里結束。但是,當然,我們不禁為您中最好奇的人提供更多詳細信息。您如何檢查公式的兩個版本之間的行為差異?
通過使用DAX Studio,您可以分析此查詢的服務器時間:
-- -- This version uses FIRSTDATE and LASTDATE -- EVALUATE SUMMARIZECOLUMNS ( 'Date'[Year Month], "Days in period", [Days in period] )
盡管速度非常快,但是可以從服務器計時中看到引擎必須兩次實現Date表:一次用於Date [Date]列,一次用於兩列Date [Date]和Date [Calendar Year Month],生成兩個具有2,556行的數據緩存。公式引擎(FE)隨后掃描這些數據緩存以計算所需的結果。
通過這種簡單的計算,當查詢使用優化版本MIN MAX內的天數 時,查詢不會更快。仍然,在實現方面要好得多,因為將整個計算下推到存儲引擎(SE),該引擎將生成具有87行的單個數據緩存:與查詢結果相同的行數。因此,期間MIN MAX中的天數將通過SE執行的完整計算產生最佳實現。在較大的模型或更復雜的場景中,實現上的這種微小差異可能會產生巨大的影響。
請注意,大多數時間智能功能(例如FIRSTDATE,LASTDATE,NEXTDAY,PREVIOUSDAY …)都顯示相同的行為:它們返回一個可以自動轉換為標量值的表。但是轉換的代價是不值得付出的。
了解函數的一些本質也是變相的改善報告的性能,就也是為什么我在之前的文章中一再強調某些函數是如何運算,可能很多小伙伴只注重結果正確而忽略了一些細節。