共用方式為


了解 ORDERBY、PARTITIONBY 和 MATCHBY 函式

DAX 中的 ORDERBYPARTITIONBYMATCHBY 函式是特殊函式,只能與 DAX 視窗函式搭配使用:INDEXOFFSETWINDOWRANKROWNUMBER

了解 ORDERBY、PARTITIONBY 和 MATCHBY 對於成功使用視窗函式非常重要。 此處提供的範例使用 OFFSET,但同樣適用於其他視窗函式。

案例

讓我們從完全不使用視窗函式的範例開始。 如下所示的資料表會傳回銷售總量,依色彩和行事曆年度區分。 有多種方式可以定義此資料表,但是由於我們有興趣了解 DAX 中發生的情況,因此我們將會使用導出資料表。 以下是資料表運算式:

BasicTable = 
    SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
    )

您會看到這個導出資料表運算式使用 SUMMARIZECOLUMNS,根據 DimProduct 資料表中的 Color 資料行,以及 DimDate 資料表中的 CalendarYear 資料行,計算 FactInternetSales 資料表中 SalesAmount 資料行的 SUM。 結果如下:

色彩 CalendarYear CurrentYearSales
"Black" 2017 393885
"Black" 2018 1818835
"Black" 2019 3981638
"Black" 2020 2644054
"Blue" 2019 994448
"Blue" 2020 1284648
"Multi" 2019 48622
"Multi" 2020 57849
"NA" 2019 207822
"NA" 2020 227295
"Red" 2017 2961198
"Red" 2018 3686935
"Red" 2019 900175
"Red" 2020 176022
"Silver" 2017 326399
"Silver" 2018 750026
"Silver" 2019 2165176
"Silver" 2020 1871788
"White" 2019 2517
"White" 2020 2589
"Yellow" 2018 163071
"Yellow" 2019 2072083
"Yellow" 2020 2621602

現在,假設我們正在嘗試解決計算每個色彩逐年銷售差異的商務問題。 實際上,我們需要一種方法來尋找去年相同色彩的銷售,並在內容中從當年的銷售中減去。 例如,針對 [Red, 2019] 的組合,我們要尋找 [Red, 2018] 的銷售。 找到該項目之後,就可以從目前的銷售減去,並傳回所需的值。

使用 OFFSET

OFFSET 非常適合回答上述商務問題所需的典型與上一個比較類型計算,因為其可讓我們進行相對移動。 我們的第一次嘗試可能是:

1stAttempt = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "PreviousColorSales",
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation
            ),
            [CurrentYearSales]
        )
    )

使用這個運算式發生很多情況。 我們使用 ADDCOLUMNS 在名為 PreviousColorSales 的資料行前面擴展資料表。 該資料行的內容會設定為 CurrentYearSales,這是 SUM(FactInternetSales[SalesAmount]),用於先前 Color (使用 OFFSET 擷取)。

結果如下:

色彩 CalendarYear CurrentYearSales PreviousColorSales
"Black" 2017 393885
"Black" 2018 1818835 393885
"Black" 2019 3981638 1818835
"Black" 2020 2644054 3981638
"Blue" 2019 994448 2644054
"Blue" 2020 1284648 994448
"Multi" 2019 48622 1284648
"Multi" 2020 57849 48622
"NA" 2019 207822 57849
"NA" 2020 227295 207822
"Red" 2017 2961198 227295
"Red" 2018 3686935 2961198
"Red" 2019 900175 3686935
"Red" 2020 176022 900175
"Silver" 2017 326399 176022
"Silver" 2018 750026 326399
"Silver" 2019 2165176 750026
"Silver" 2020 1871788 2165176
"White" 2019 2517 1871788
"White" 2020 2589 2517
"Yellow" 2018 163071 2589
"Yellow" 2019 2072083 163071
"Yellow" 2020 2621602 2072083

這讓我們離目標更近一步,但是如果我們仔細看,這其實沒有確實符合我們追求的內容。 例如,針對 [Silver, 2017],PreviousColorSales 設定為 [Red, 2020]。

新增 ORDERBY

上述定義相當於:

1stAttemptWithORDERBY = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "PreviousColorSales",
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([Color], ASC, [CalendarYear], ASC, [CurrentYearSales], ASC)      
            ),
            [CurrentYearSales]
        )
    )

在此案例中,對 OFFSET 的呼叫會使用 ORDERBY 依 Color 和 CalendarYear 遞增排序資料表,這會決定所傳回前一個資料列被當作什麼項目。

這兩個結果相等的原因是 ORDERBY 會自動包含不在 PARTITIONBY 中的關聯中的所有資料行。 由於未指定 PARTITIONBY,所以 ORDERBY 會設定為 Color、CalendarYear 和 CurrentYearSales。 不過,由於關聯中的 Color 和 CalendarYear 配對是唯一的,因此新增 CurrentYearSales 不會變更結果。 事實上,即使我們只在 ORDERBY 中指定 Color,結果也會相同,因為 CalendarYear 會自動新增。 這是因為函式會視需要盡可能將更多資料行加入 ORDERBY,以確保 ORDERBY 和 PARTITIONBY 資料行可以唯一識別每個資料列:

1stAttemptWithORDERBY = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS(
        vRelation,
        "PreviousColorSales",
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([Color])
            ),
            [CurrentYearSales]
        )
    )

新增 PARTITIONBY

現在,為了幾近取得我們追求的結果,我們可以使用 PARTITIONBY,如同下列導出資料表運算式所示:

UsingPARTITIONBY = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "PreviousColorSales",
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([CalendarYear]), 
                PARTITIONBY ([Color])
            ),
            [CurrentYearSales]
        )
    )

請注意,在這裡指定 ORDERBY 是選擇性的,因為 ORDERBY 會自動包含未在 PARTITIONBY 中指定的關聯的所有資料行。 因此,下列運算式會傳回相同的結果,因為 ORDERBY 會自動設定為 CalendarYear 和 CurrentYearSales:

UsingPARTITIONBYWithoutORDERBY = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "PreviousColorSales",
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                PARTITIONBY ([Color])
            ),
            [CurrentYearSales]
        )
    )


注意

雖然 ORDERBY 會自動設定為 CalendarYear 和 CurrentYearSales,但是不保證新增的順序。 如果在 CalendarYear 之前新增 CurrentYearSales,則產生的順序不會與預期的一致。 指定 ORDERBY 和 PARTITIONBY 時明確,以避免混淆和非預期的結果

這兩個運算式都會傳回我們追求的結果:

色彩 CalendarYear CurrentYearSales PreviousYearSalesForSameColor
"Black" 2017 393885
"Black" 2018 1818835 393885
"Black" 2019 3981638 1818835
"Black" 2020 2644054 3981638
"Blue" 2019 994448
"Blue" 2020 1284648 994448
"Multi" 2019 48622
"Multi" 2020 57849 48622
"NA" 2019 207822
"NA" 2020 227295 207822
"Red" 2017 2961198
"Red" 2018 3686935 2961198
"Red" 2019 900175 3686935
"Red" 2020 176022 900175
"Silver" 2017 326399
"Silver" 2018 750026 326399
"Silver" 2019 2165176 750026
"Silver" 2020 1871788 2165176
"White" 2019 2517
"White" 2020 2589 2517
"Yellow" 2018 163071
"Yellow" 2019 2072083 163071
"Yellow" 2020 2621602 2072083

如您在此資料表中所見,PreviousYearSalesForSameColor 資料行會以相同色彩顯示前一年的銷售。 針對 [Red, 2020],會傳回 [Red, 2019] 的銷售,依此類推。 如果沒有前一年,例如 [Red, 2017] 的情況,則不會傳回任何值。

您可以將 PARTITIONBY 視為一種方法,將資料表分割成執行 OFFSET 計算的部分。 在上述範例中,資料表會分割成有任意色彩的多個部分,每種色彩各有一個。 然後,在每個部分內,OFFSET 會進行計算,依 CalendarYear 排序。

視覺上發生的情況如下:

顯示依日曆年度 OFFSET 的資料表

首先,對 PARTITIONBY 的呼叫會導致資料表分割成許多部分,每個 Color 各有一個。 在資料表影像中會以淺藍色方塊表示。 接下來,ORDERBY 可確保每個部分都依 CalendarYear 排序 (以橙色箭號表示)。 最後,在每個已排序的部分中,OFFSET 會尋找其上方的每個資料列,並在 PreviousYearSalesForSameColor 資料行中傳回該值。 由於針對每個部分中的每個第一個資料列,沒有上一個資料列,因此 PreviousYearSalesForSameColor 資料行的結果是空的。

為了達到最終結果,我們只需要從前一年的銷售減去 OFFSET 呼叫所傳回相同色彩的 CurrentYearSales。 因為我們不想要顯示相同色彩的前一年銷售,而只是要顯示當年銷售和逐年差異。 以下是最終導出資料表運算式:

FinalResult = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "YoYSalesForSameColor",
        [CurrentYearSales] -
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([CalendarYear]),
                PARTITIONBY ([Color])
            ),
            [CurrentYearSales]
        )
    )

以下是該運算式的結果:

色彩 CalendarYear CurrentYearSales YoYSalesForSameColor
"Black" 2017 393885 393885
"Black" 2018 1818835 1424950
"Black" 2019 3981638 2162803
"Black" 2020 2644054 -1337584
"Blue" 2019 994448 994448
"Blue" 2020 1284648 290200
"Multi" 2019 48622 48622
"Multi" 2020 57849 9227
"NA" 2019 207822 207822
"NA" 2020 227295 19473
"Red" 2017 2961198 2961198
"Red" 2018 3686935 725737
"Red" 2019 900175 -2786760
"Red" 2020 176022 -724153
"Silver" 2017 326399 326399
"Silver" 2018 750026 423627
"Silver" 2019 2165176 1415150
"Silver" 2020 1871788 -293388
"White" 2019 2517 2517
"White" 2020 2589 72
"Yellow" 2018 163071 163071
"Yellow" 2019 2072083 1909012
"Yellow" 2020 2621602 549519

使用 MATCHBY

您可能已經注意到我們完全沒有指定 MATCHBY。 在此案例中,這並非必要。 ORDERBY 和 PARTITIONBY 中的資料行 (就上述範例中指定的資料行而言) 就足以唯一識別每個資料列。 因為我們未指定 MATCHBY,所以 ORDERBY 和 PARTITIONBY 中指定的資料行會用來唯一識別每個資料列,以便進行比較以啟用 OFFSET 來提供有意義的結果。 如果 ORDERBY 和 PARTITIONBY 中的資料行無法唯一識別每個資料列,若這些額外的資料行允許唯一識別每個資料列,可以將其他資料行新增至 ORDERBY 子句。 如果不可行,則會傳回錯誤。 在此最後一個案例中,指定 MATCHBY 可能有助於解決錯誤。

如果指定 MATCHBY,MATCHBY 和 PARTITIONBY 中的資料行會用來唯一識別每個資料列。 如果不可行,則會傳回錯誤。 即使不需要 MATCHBY,也請考慮明確指定 MATCHBY 以避免任何混淆。

繼續上述範例,以下是最後一個運算式:

FinalResult = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "YoYSalesForSameColor",
        [CurrentYearSales] -
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([CalendarYear]),
                PARTITIONBY ([Color])
            ),
            [CurrentYearSales]
        )
    )

如果我們想要讓應該如何唯一識別資料列更為明確,我們可以指定 MATCHBY,如下列對等運算式所示:

FinalResultWithExplicitMATCHBYOnColorAndCalendarYear = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "YoYSalesForSameColor",
        [CurrentYearSales] -
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([CalendarYear]),
                PARTITIONBY ([Color]),
                MATCHBY ([Color], [CalendarYear])
            ),
            [CurrentYearSales]
        )
    )

由於指定 MATCHBY,因此 MATCHBY 和 PARTITIONBY 中指定的資料行都會用來唯一識別資料列。 由於 COLOR 同時在 MATCHBY 和 PARTITIONBY 中指定,因此下列運算式相當於上一個運算式:

FinalResultWithExplicitMATCHBYOnCalendarYear = 
    VAR vRelation = SUMMARIZECOLUMNS ( 
        DimProduct[Color], 
        DimDate[CalendarYear], 
        "CurrentYearSales", ROUND ( SUM ( FactInternetSales[SalesAmount] ), 0 )
        )
    RETURN
    ADDCOLUMNS (
        vRelation,
        "YoYSalesForSameColor",
        [CurrentYearSales] -
        SELECTCOLUMNS (
            OFFSET (
                -1,
                vRelation,
                ORDERBY ([CalendarYear]),
                PARTITIONBY ([Color]),
                MATCHBY ([CalendarYear])
            ),
            [CurrentYearSales]
        )
    )

由於我們目前為止所探討的範例中不需要指定 MATCHBY,因此讓我們看看需要 MATCHBY 的稍有不同的範例。 在此案例中,我們有訂單明細行清單。 每個資料列都代表訂單的訂單明細行。 訂單可以有多個訂單明細行,而訂單明細行 1 會顯示在許多訂單上。 此外,針對每個訂單明細行,我們有 ProductKey 和 SalesAmount。 資料表上相關資料行的範例如下所示:

SalesOrderNumber SalesOrderLineNumber ProductKey SalesAmount
SO51900 1 528 4.99
SO51948 1 528 5.99
SO52043 1 528 4.99
SO52045 1 528 4.99
SO52094 1 528 4.99
SO52175 1 528 4.99
SO52190 1 528 4.99
SO52232 1 528 4.99
SO52234 1 528 4.99
SO52234 2 529 3.99

請注意,必須有 SalesOrderNumber 和 SalesOrderLineNumber 才能唯一識別資料列。

針對每個訂單,我們想要依據 SalesAmount 以遞減順序傳回所訂購相同產品 (以 ProductKey 表示) 的先前銷售金額。 下列運算式無法運作,因為當 vRelation 傳入 OFFSET 時其中可能有多個資料列:

ThisExpressionFailsBecauseMATCHBYIsMissing = 
    ADDCOLUMNS (
        FactInternetSales,
        "Previous Sales Amount",
            SELECTCOLUMNS (
                OFFSET (
                    -1,
                    FactInternetSales,
                    ORDERBY ( FactInternetSales[SalesAmount], DESC ),
                    PARTITIONBY ( FactInternetSales[ProductKey] )
                ),
                FactInternetSales[SalesAmount]
            )
    )

此運算式會傳回錯誤:「OFFSET 的 Relation 參數可能有重複的資料列,這是不允許的。」

若要讓此運算式正常運作,必須指定 MATCHBY,而且必須包含唯一定義資料列的所有資料行。 這裡需要 MATCHBY,因為關聯 FactInternetSales 不包含任何明確的索引鍵或唯一資料行。 不過,SalesOrderNumber 和 SalesOrderLineNumber 資料行會形成複合索引鍵,它們同時存在在關聯中是唯一的,因此可以唯一識別每個資料列。 只有指定 SalesOrderNumber 或 SalesOrderLineNumber 是不夠的,因為兩個資料行都包含重複的值。 下列運算式可解決問題:

ThisExpressionWorksBecauseOfMATCHBY = 
    ADDCOLUMNS (
        FactInternetSales,
        "Previous Sales Amount",
            SELECTCOLUMNS (
                OFFSET (
                    -1,
                    FactInternetSales,
                    ORDERBY ( FactInternetSales[SalesAmount], DESC ),
                    PARTITIONBY ( FactInternetSales[ProductKey] ),
                    MATCHBY ( FactInternetSales[SalesOrderNumber], 
                                FactInternetSales[SalesOrderLineNumber] )
                ),
                FactInternetSales[SalesAmount]
            )
    )

而此運算式確實會傳回我們追求的結果:

SalesOrderNumber SalesOrderLineNumber ProductKey SalesAmount 先前銷售數量
SO51900 1 528 5.99
SO51948 1 528 4.99 5.99
SO52043 1 528 4.99 4.99
SO52045 1 528 4.99 4.99
SO52094 1 528 4.99 4.99
SO52175 1 528 4.99 4.99
SO52190 1 528 4.99 4.99
SO52232 1 528 4.99 4.99
SO52234 1 528 4.99 4.99
SO52234 2 529 3.99

ORDERBY
PARTITIONBY
MATCHBY
INDEX
OFFSET
WINDOW
RANK
ROWNUMBER