DAX從入門到精通?5-5 明白什么是循環(huán)引用

當你設(shè)計一個數(shù)據(jù)模型,你應(yīng)該要注意一個復雜的概念,也就是公式的循環(huán)引用。這節(jié)中,我們要學習什么是循環(huán)引用,以及如何避免。

介紹循環(huán)引用概念之前,我們先介紹一些簡單的,線性的依賴關(guān)系。我們看下如下的計算列。

Product[Profit] = Product[Unit Price] - Product[Unit Cost]

這個計算列依賴同一個表的兩個列。這個情況下,我們說Profit列依賴于unit price和unit cost。接著,你要定義一個新的列。例如ProPct,使用的代碼如下:

Product[ProfitPct] = Product[Profit] / Product[Unit Price]

可以很簡單的看出來,ProfitPct依賴于Profit和Uint Price,因此,當DAX在表中計算計算列的時候。它知道要先計算Profit才能計算ProfitPct,否則,這個函數(shù)就不能正確的計算。

線性的依賴關(guān)系不是通常我們需要考慮的問題。在DAX內(nèi)部,當數(shù)據(jù)模型刷新的時候,DAX會自動檢測正確的計算順序。即使是復雜的,包含很多計算列的數(shù)據(jù)模型,DAX也會很輕松的解決。

循環(huán)引用是當計算中使用循環(huán)的時候,發(fā)生的一種問題。例如,這樣一個簡單情況就是,當你稍微修改以下上面的計算公式:

Product[Profit] := Product[ProfitPct] * Product[Unit Price]

因為ProfitPct依賴于Profit,這個新的函數(shù)中,Profit依賴了ProfitPct,這樣的話,DAX就會拒絕修改函數(shù)并且提示一個錯誤:“A circular dependency was detected”,發(fā)現(xiàn)循環(huán)引用。

目前為止,我們所學的循環(huán)引用都是在函數(shù)使用角度來說。我們發(fā)現(xiàn)問題總是只基于表達式,而沒有注意到表內(nèi)容。這里還有一種循環(huán)引用,它發(fā)生的更隱蔽,是只用calculate的時候可能會發(fā)生的。我們用一個案例來介紹一下這個場景,我們使用product的一個子集來說明問題,如下圖,作為說明問題用我們只加載了Product表,然后移除了模型中的其他表。這個是為了使場景簡單話。


image.png

我們用一個新的計算列,值使用calculate來計算,來看看循環(huán)引用的問題,代碼如下:

Product[SumOfUnitPrice] = CALCULATE ( SUM ( Product[Unit
Price] ) )

第一眼看去,這列只有依賴于Unit Price,因為這個是公式中僅有的列。不過,因為我們使用calculate的時候,會把行上下文自動轉(zhuǎn)換為篩選上下文。我們沒有和其他表格定義任何關(guān)系,也沒有給表格定義主鍵,所以當calculate轉(zhuǎn)換的時候,它會對表格的所有列生成篩選條件。如果我們把calculate函數(shù)展開解釋,意思就是這樣:

對product表的所有列篩選,把具有相同productkey,product name,unit cost,unit price的記錄值相加起來。
如果你是這樣閱讀這個函數(shù),那么很明顯,這個代碼依賴于product的所有列,因為新生成的篩選上下文會自動過濾表格的所有列,我們在下面的表格中可以看到結(jié)果。


image.png

你可能還會去定義一個新的計算列,我們使用了類似的函數(shù),也是在這個表中。定義一個newsumofunitprice,代碼如下:

Product[NewSumOfUnitPrice] = CALCULATE ( SUM ( Product[Unit
Price] ) )

讓人驚奇的是,這次,DAX報了一個錯誤,說發(fā)現(xiàn)了一個循環(huán)引用。而這個公式在上一次使用的時候,還是正常工作的。事實上,第二次的時候,情況已經(jīng)發(fā)生了改變,改變的是現(xiàn)在表格中的列。如果我們把newsumofunitprice添加到表個,那么我們得到的意思是這樣:

sumofunitprice對product表中 具有和當前行相同的 productkey,productname,unit cost,unit price以及newsumofunitprice的記錄的unit price求和。

newsumofunitprice對product表中所和當前行具有相同productkey,productname,unitcost,unitprice和sumofunitprice的行的值進行求和。

計算列和表格中的其他列一樣,在使用calculate的時候,都會成為篩選條件的一部分。因此,所有的計算列都會是依賴條件的一部分。如果看了前面的定義,就會明白兩列的之前的循環(huán)依賴關(guān)系,這個就是為什么DAX拒絕新建newsumofunitprice的原因。

要解決這個問題很簡單。這里的問題是因為表沒有主鍵,然后其他使用calculate的計算列都建立上下文轉(zhuǎn)換的時候,都會把所有的列考慮進去,包括計算列。但是如果這時候表中有主鍵,那么情況就會不同。即如果某列是主鍵,那么所有的使用calculate的計算列,都會依賴于這個主鍵列,而不是表的所有列。

在product表中,productkey列可以作為主鍵列。要定義該列為行的標識,可以有兩種方式:

可以把productkey列作為關(guān)系的一方,定義一個關(guān)系。通過這樣操作可以使得productkey作為一個唯一值。
可以手動在表格屬性中定義,把productkey定義為表格的主鍵:

這兩種方式都會讓DAX知道表格有主鍵。這樣的的情況下,我們定義newsumofunitprice列不會出現(xiàn)循環(huán)引用的錯誤,因為兩個計算列中的calculate都是依賴于主鍵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容