Del via


Om funktionerne ORDERBY, PARTITIONBYog MATCHBY

Funktionerne ORDERBY, PARTITIONBYog MATCHBY i DAX er specialfunktioner, der kun kan bruges sammen med funktionerne DAX Window: INDEX, OFFSET, WINDOW, RANK, ROWNUMBER.

Det er vigtigt at forstå ORDERBY, PARTITIONBYog MATCHBY for at kunne bruge funktionerne Vindue. De eksempler, der er angivet her, bruger OFFSET, men gælder også for de andre vinduesfunktioner.

Scenario

Lad os starte med et eksempel, der slet ikke bruger vinduesfunktioner. Vist nedenfor er en tabel, der returnerer det samlede salg pr. farve pr. kalenderår. Der er flere måder at definere denne tabel på, men da vi er interesseret i at forstå, hvad der sker i DAX, bruger vi en beregnet tabel. Her er tabeludtrykket:

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

Du kan se, at dette beregnede tabeludtryk bruger SUMMARIZECOLUMNS til at beregne SUM af kolonnen SalesAmount i tabellen FactInternetSales efter kolonnen Color fra tabellen DimProduct og kolonnen CalendarYear fra tabellen DimDate. Her er resultatet:

Farve Kalenderår CurrentYearSales
"Sort" 2017 393885
"Sort" 2018 1818835
"Sort" 2019 3981638
"Sort" 2020 2644054
"Blå" 2019 994448
"Blå" 2020 1284648
"Flere" 2019 48622
"Flere" 2020 57849
"NA" 2019 207822
"NA" 2020 227295
"Rød" 2017 2961198
"Rød" 2018 3686935
"Rød" 2019 900175
"Rød" 2020 176022
"Sølv" 2017 326399
"Sølv" 2018 750026
"Sølv" 2019 2165176
"Sølv" 2020 1871788
"Hvid" 2019 2517
"Hvid" 2020 2589
"Gul" 2018 163071
"Gul" 2019 2072083
"Gul" 2020 2621602

Lad os nu forestille os, at vi forsøger at løse det forretningsmæssige spørgsmål om at beregne forskellen i salg år for år for hver farve. Faktisk har vi brug for en måde at finde salg for samme farve i det foregående år og trække det fra salget i det aktuelle år, i kontekst. For kombinationen [Red, 2019] leder vi f.eks. efter salg for [Red, 2018]. Når vi har det, kan vi derefter trække det fra det aktuelle salg og returnere den påkrævede værdi.

Brug af OFFSET

OFFSET er perfekt til de typiske sammenligne med tidligere typer beregninger, der kræves for at besvare det forretningsspørgsmål, der er beskrevet ovenfor, da det giver os mulighed for at foretage en relativ bevægelse. Vores første forsøg kan være:

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

Der sker meget med dette udtryk. Vi brugte ADDCOLUMNS til at udvide tabellen fra før med en kolonne med navnet PreviousColorSales. Indholdet af kolonnen er angivet til CurrentYearSales, som er SUM(FactInternetSales[SalesAmount]) for den forrige Farve (hentet ved hjælp af OFFSET).

Resultatet er:

Farve Kalenderår CurrentYearSales PreviousColorSales
"Sort" 2017 393885
"Sort" 2018 1818835 393885
"Sort" 2019 3981638 1818835
"Sort" 2020 2644054 3981638
"Blå" 2019 994448 2644054
"Blå" 2020 1284648 994448
"Flere" 2019 48622 1284648
"Flere" 2020 57849 48622
"NA" 2019 207822 57849
"NA" 2020 227295 207822
"Rød" 2017 2961198 227295
"Rød" 2018 3686935 2961198
"Rød" 2019 900175 3686935
"Rød" 2020 176022 900175
"Sølv" 2017 326399 176022
"Sølv" 2018 750026 326399
"Sølv" 2019 2165176 750026
"Sølv" 2020 1871788 2165176
"Hvid" 2019 2517 1871788
"Hvid" 2020 2589 2517
"Gul" 2018 163071 2589
"Gul" 2019 2072083 163071
"Gul" 2020 2621602 2072083

Dette er et skridt tættere på vores mål, men hvis vi ser nærmere på det, stemmer det ikke nøjagtigt overens med det, vi er ude efter. For [Silver, 2017] er PreviousColorSales f.eks. angivet til [Red, 2020].

Tilføjelse af ORDERBY

Ovenstående definition svarer til:

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]
        )
    )

I dette tilfælde bruger kaldet til OFFSETORDERBY til at sortere tabellen efter Farve og CalendarYear i stigende rækkefølge, hvilket bestemmer, hvad der anses for at være den forrige række, der returneres.

Årsagen til, at disse to resultater svarer til hinanden, er, at ORDERBY automatisk indeholder alle kolonner fra relationen, der ikke findes i PARTITIONBY. Da PARTITIONBY ikke blev angivet, er ORDERBY angivet til Color, CalendarYear og CurrentYearSales. Men da parene Color og CalendarYear i relationen er entydige, ændrer tilføjelsen af CurrentYearSales ikke resultatet. Selvom vi kun skulle angive Farve i ORDERBY, er resultaterne faktisk de samme, da CalendarYear automatisk tilføjes. Det skyldes, at funktionen tilføjer lige så mange kolonner, der er nødvendige for at ORDERBY, for at sikre, at hver række kan identificeres entydigt af kolonnerne ORDERBY og 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]
        )
    )

Tilføjelse af PARTITIONBY

Hvis du nu vil næsten få det resultat, vi er ude efter, kan vi bruge PARTITIONBY, som vist i følgende beregnede tabeludtryk:

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]
        )
    )

Bemærk, at det er valgfrit at angive ORDERBY her, fordi ORDERBY automatisk indeholder alle de kolonner fra relationen, der ikke er angivet i PARTITIONBY. Følgende udtryk returnerer derfor de samme resultater, fordi ORDERBY automatisk er angivet til CalendarYear og 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]
        )
    )

Seddel

Selvom ORDERBY er angivet til CalendarYear og CurrentYearSales automatisk, gives der ingen garanti for, hvilken rækkefølge de vil blive tilføjet i. Hvis CurrentYearSales tilføjes før CalendarYear, er den resulterende ordre ikke i overensstemmelse med det, der forventes. Vær eksplicit, når du angiver ORDERBY og PARTITIONBY for at undgå forvirring og uventede resultater.

Begge udtryk returnerer det resultat, vi er efter:

Farve Kalenderår CurrentYearSales PreviousYearSalesForSameColor
"Sort" 2017 393885
"Sort" 2018 1818835 393885
"Sort" 2019 3981638 1818835
"Sort" 2020 2644054 3981638
"Blå" 2019 994448
"Blå" 2020 1284648 994448
"Flere" 2019 48622
"Flere" 2020 57849 48622
"NA" 2019 207822
"NA" 2020 227295 207822
"Rød" 2017 2961198
"Rød" 2018 3686935 2961198
"Rød" 2019 900175 3686935
"Rød" 2020 176022 900175
"Sølv" 2017 326399
"Sølv" 2018 750026 326399
"Sølv" 2019 2165176 750026
"Sølv" 2020 1871788 2165176
"Hvid" 2019 2517
"Hvid" 2020 2589 2517
"Gul" 2018 163071
"Gul" 2019 2072083 163071
"Gul" 2020 2621602 2072083

Som du kan se i denne tabel, viser kolonnen PreviousYearSalesForSameColor salget for det foregående år for den samme farve. For [Red, 2020] returnerer den salget for [Red, 2019] osv. Hvis der ikke er noget forrige år, f.eks. i tilfælde af [Red, 2017], returneres der ingen værdi.

Du kan tænke på PARTITIONBY som en måde at opdele tabellen i dele, hvor du kan udføre OFFSET beregningen. I eksemplet ovenfor er tabellen opdelt i lige så mange dele, som der er farver, én for hver farve. Derefter beregnes OFFSET i hver del sorteret efter CalendarYear.

Visuelt, hvad der sker, er dette:

tabel, der viser OFFSET efter kalenderår

Først medfører kaldet til PARTITIONBY, at tabellen opdeles i dele, én for hver Farve. Dette repræsenteres af de lyseblå felter på tabelbilledet. Derefter sørger ORDERBY for, at hver del sorteres efter CalendarYear (repræsenteret af de orange pile). Til sidst finder OFFSET rækken over hver sorteret del for hver række og returnerer denne værdi i kolonnen PreviousYearSalesForSameColor. Da der for hver første række i hver del ikke er nogen forrige række i den samme del, er resultatet i den pågældende række for kolonnen PreviousYearSalesForSameColor tom.

For at opnå det endelige resultat skal vi blot trække CurrentYearSales fra det forrige års salg for den samme farve, der returneres af kaldet til OFFSET. Da vi ikke er interesseret i at vise det forrige års salg for samme farve, men kun i det aktuelle års salg og forskellen år for år. Her er det endelige beregnede tabeludtryk:

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]
        )
    )

Og her er resultatet af dette udtryk:

Farve Kalenderår CurrentYearSales YoYSalesForSameColor
"Sort" 2017 393885 393885
"Sort" 2018 1818835 1424950
"Sort" 2019 3981638 2162803
"Sort" 2020 2644054 -1337584
"Blå" 2019 994448 994448
"Blå" 2020 1284648 290200
"Flere" 2019 48622 48622
"Flere" 2020 57849 9227
"NA" 2019 207822 207822
"NA" 2020 227295 19473
"Rød" 2017 2961198 2961198
"Rød" 2018 3686935 725737
"Rød" 2019 900175 -2786760
"Rød" 2020 176022 -724153
"Sølv" 2017 326399 326399
"Sølv" 2018 750026 423627
"Sølv" 2019 2165176 1415150
"Sølv" 2020 1871788 -293388
"Hvid" 2019 2517 2517
"Hvid" 2020 2589 72
"Gul" 2018 163071 163071
"Gul" 2019 2072083 1909012
"Gul" 2020 2621602 549519

Brug af MATCHBY

Du har måske bemærket, at vi slet ikke har angivet MATCHBY. I dette tilfælde er det ikke nødvendigt. Kolonnerne i ORDERBY og PARTITIONBY (for så vidt de er angivet i ovenstående eksempler) er tilstrækkelige til entydigt at identificere hver række. Da vi ikke har angivet MATCHBY, bruges de kolonner, der er angivet i ORDERBY og PARTITIONBY, til entydigt at identificere hver række, så de kan sammenlignes, så OFFSET giver et meningsfuldt resultat. Hvis kolonnerne i ORDERBY og PARTITIONBY ikke entydigt kan identificere hver række, kan der føjes flere kolonner til delsætningen ORDERBY, hvis disse ekstra kolonner gør det muligt at identificere hver række entydigt. Hvis det ikke er muligt, returneres der en fejl. I dette sidste tilfælde kan angivelse af MATCHBY hjælpe med at løse fejlen.

Hvis MATCHBY er angivet, bruges kolonnerne i MATCHBY og PARTITIONBY til entydigt at identificere hver række. Hvis det ikke er muligt, returneres der en fejl. Selvom MATCHBY ikke er påkrævet, kan du overveje eksplicit at angive MATCHBY for at undgå forvirring.

Hvis du fortsætter fra eksemplerne ovenfor, er dette det sidste udtryk:

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]
        )
    )

Hvis vi vil være eksplicitte om, hvordan rækker skal identificeres entydigt, kan vi angive MATCHBY som vist i følgende tilsvarende udtryk:

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]
        )
    )

Da MATCHBY er angivet, bruges både de kolonner, der er angivet i MATCHBY, samt i PARTITIONBY til entydigt at identificere rækker. Da Color er angivet i både MATCHBY og PARTITIONBY, svarer følgende udtryk til det forrige udtryk:

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]
        )
    )

Da det ikke er nødvendigt at angive MATCHBY i de eksempler, vi har kigget på indtil videre, kan vi se på et lidt andet eksempel, der kræver MATCHBY. I dette tilfælde har vi en liste over ordrelinjer. Hver række repræsenterer en ordrelinje for en ordre. En ordre kan have flere ordrelinjer, og ordrelinje 1 vises på mange ordrer. Derudover har vi for hver ordrelinje en ProductKey og en SalesAmount. Et eksempel på de relevante kolonner i tabellen ser sådan ud:

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

Bemærk, at Både SalesOrderNumber og SalesOrderLineNumber er nødvendige for entydigt at identificere rækker.

For hver ordre vil vi returnere det tidligere salgsbeløb for det samme produkt (repræsenteret af ProductKey), der er bestilt af SalesAmount i faldende rækkefølge. Følgende udtryk fungerer ikke, fordi der muligvis er flere rækker i vRelation, da det overføres til OFFSET:

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

Dette udtryk returnerer en fejl: "OFFSET's relationsparameter kan have dublerede rækker, hvilket ikke er tilladt."

Hvis dette udtryk skal fungere, skal MATCHBY angives og skal indeholde alle kolonner, der entydigt definerer en række. MATCHBY er påkrævet her, fordi relationen FactInternetSales ikke indeholder eksplicitte nøgler eller entydige kolonner. Kolonnerne SalesOrderNumber og SalesOrderLineNumber tilsammen udgør dog en sammensat nøgle, hvor deres eksistens sammen er entydig i relationen og derfor entydigt kan identificere hver række. Det er ikke nok blot at angive SalesOrderNumber eller SalesOrderLineNumber, da begge kolonner indeholder gentagne værdier. Følgende udtryk løser problemet:

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

Og dette udtryk returnerer faktisk de resultater, vi er ude efter:

SalesOrderNumber SalesOrderLineNumber ProductKey SalesAmount Tidligere salgsbeløb
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