Blog JSystems - uwalniamy wiedzę!

Szukaj

Power BI - DAX liczy, ale nie to, czego oczekujesz

Kolumna obliczeniowa z CALCULATE liczy coś innego niż bez niej - przejście kontekstu

W skrócie

  • Kolumna obliczeniowa z samym SUM pokazuje w każdym wierszu sumę całej tabeli, a ta sama formuła w CALCULATE daje wynik bieżącego wiersza. Różnicę robi przejście kontekstu w DAX.
  • Kolumna obliczeniowa ma kontekst wiersza, ale agregacja widzi tylko kontekst filtra. CALCULATE zamienia bieżący wiersz na filtr, a relacja przenosi go do faktów. Miara w iteratorze robi to automatycznie.
  • W tabeli wymiaru agregację owiń w CALCULATE albo RELATEDTABLE. W iteratorach używaj miary. W tabeli faktów nie owijaj wartości wiersza w CALCULATE, a sumę grupy licz raz w wymiarze i pobieraj przez RELATED.

Przejście kontekstu w DAX zaskakuje każdego, kto pierwszy raz dopisuje do tabeli kolumnę obliczeniową z sumą: formuła bez CALCULATE pokazuje w każdym wierszu sprzedaż całej firmy, a z CALCULATE sprzedaż jednego sklepu. Ten sam mechanizm decyduje o wyniku SUMX, FILTER i AVERAGEX z miarą w środku. Pokazujemy na modelu Nordvelli, co dokładnie robi CALCULATE w kontekście wiersza, gdzie to pomaga, a gdzie tylko kosztuje.

Jak to wygląda w praktyce

W tabeli DimSklepy są dwie kolumny obliczeniowe z kursu. Sprzedaż sklepu (zła) = SUM(FaktSprzedaz[WartoscNetto]) ma w każdym z 15 wierszy 467 775 009,17 zł, czyli sprzedaż całej sieci. Sprzedaż sklepu = CALCULATE(SUM(FaktSprzedaz[WartoscNetto])) daje kwotę sklepu: Warszawa Centrum 34 740 655,15 zł, Warszawa Mokotów 25 320 459,78 zł, Kraków Stare Miasto 31 545 739,77 zł. Zła kolumna przeciągnięta do wizualizacji z regionami sumuje się do 1 871 100 036,68 zł dla regionu Centrum, czyli do czterokrotności sprzedaży sieci, bo region ma cztery sklepy.

Ten sam objaw pojawia się w miarach z iteratorem. SUMX(DimSklepy, SUM(FaktSprzedaz[WartoscNetto])) zwraca 7 016 625 137,55 zł, piętnaście razy sprzedaż sieci, a FILTER(DimSklepy, SUM(FaktSprzedaz[WartoscNetto]) > 30000000) przepuszcza wszystkie 15 sklepów, choć próg 30 mln zł przekracza tylko pięć. Kolumna SUM(FaktSprzedaz[WartoscNetto]) w tabeli faktów ma tę samą kwotę we wszystkich 108 675 wierszach.

Widok tabeli DimSklepy w Power BI: Sprzedaż sklepu (zła) z tą samą sumą w każdym wierszu i poprawna Sprzedaż sklepu
Kolumna bez CALCULATE pokazuje w każdym sklepie tę samą sumę całej firmy, 467775009,17. Dopiero CALCULATE zamienia kontekst wiersza na filtr i daje sprzedaż tego sklepu.

Dlaczego tak się dzieje

DAX ma dwa rodzaje kontekstu. Kontekst wiersza to znajomość jednego, bieżącego wiersza, który mają kolumny obliczeniowe i iteratory, takie jak SUMX czy FILTER. Kontekst filtra to zestaw warunków ograniczających dane, który tworzą wiersze wizualizacji, fragmentatory i CALCULATE. Funkcje agregujące, na przykład SUM, widzą tylko kontekst filtra. W kolumnie obliczeniowej ten kontekst jest pusty, więc SUM liczy całą tabelę, niezależnie od tego, w którym wierszu się znajduje.

CALCULATE wywołane w kontekście wiersza wykonuje przejście kontekstu: zamienia bieżący wiersz na filtr każdej kolumny tej tabeli. W DimSklepy filtr obejmuje między innymi SklepID, a relacja jeden do wielu przenosi go do FaktSprzedaz, więc suma dotyczy jednego sklepu. Według dokumentacji Microsoft odwołanie do miary w kontekście wiersza wykonuje przejście kontekstu automatycznie. Dzieje się tak, bo każda miara jest wewnętrznie owinięta w niejawne CALCULATE. Dlatego SUMX(DimSklepy, [Łączna sprzedaż]) daje 467 775 009,17 zł, a FILTER(DimSklepy, [Łączna sprzedaż] > 30000000) zostawia pięć sklepów: Warszawa Centrum, Kraków Stare Miasto, Wrocław, Katowice i Nordvella Online.

W tabeli faktów przejście kontekstu filtruje kolumny tego samego wiersza. FaktSprzedaz ma unikatowy SprzedazID (108 675 wartości na 108 675 wierszy), więc CALCULATE(SUM(FaktSprzedaz[WartoscNetto])) zwraca dokładnie wartość wiersza. Druga taka kolumna obok kończy się komunikatem „Wykryto zależność cykliczną” (w angielskiej wersji: „A circular dependency was detected”), co opisujemy w poście o zależności cyklicznej w kolumnach obliczeniowych.

Co zrobić krok po kroku

  1. Zapisz, co kolumna ma liczyć. Wartość z bieżącego wiersza (na przykład marża transakcji) to zwykłe wyrażenie na kolumnach tego wiersza, bez agregacji i bez CALCULATE. Suma powiązanych wierszy innej tabeli (sprzedaż sklepu w DimSklepy) wymaga przejścia kontekstu. Suma grupy w tej samej tabeli faktów (sprzedaż sklepu przy każdej transakcji) wymaga przejścia kontekstu z ograniczeniem do kolumn grupy.
  2. W tabeli wymiaru owiń agregację w CALCULATE albo użyj RELATEDTABLE, które według dokumentacji Microsoft jest skrótem CALCULATETABLE bez warunków. Prawy przycisk na DimSklepy w panelu Dane, Nowa kolumna. Obie formuły dają w każdym z 15 sklepów tę samą kwotę co Sprzedaż sklepu. Liczba transakcji przez RELATEDTABLE to 8145 dla Warszawy Centrum i 6423 dla Gdańska, a COUNTROWS(FaktSprzedaz) bez niej daje 108 675 w każdym wierszu.
    Sprzedaż sklepu RT =
    SUMX ( RELATEDTABLE ( FaktSprzedaz ), FaktSprzedaz[WartoscNetto] )
    
    Transakcje sklepu =
    COUNTROWS ( RELATEDTABLE ( FaktSprzedaz ) )
  3. W mierze z iteratorem po wymiarze odwołuj się do miary albo do agregacji w CALCULATE. Średnia sprzedaż sklepu Nordvelli to 31 185 000,61 zł, a największa 141 772 848,59 zł (Nordvella Online). Te same formuły z samym SUM dają 467 775 009,17 zł. Iteratory SUMX i AVERAGEX omawiamy w lekcji o iteratorach i zmiennych VAR.
    Średnia sprzedaż sklepu =
    AVERAGEX ( DimSklepy, [Łączna sprzedaż] )
    
    Największa sprzedaż sklepu =
    MAXX ( DimSklepy, [Łączna sprzedaż] )
  4. W tabeli faktów nie owijaj wartości wiersza w CALCULATE. Kolumna CALCULATE(SUM(FaktSprzedaz[WartoscNetto])) w FaktSprzedaz nie różni się od WartoscNetto w żadnym ze 108 675 wierszy, a formuła z przejściem kontekstu liczy się osobno dla każdego wiersza przy każdym odświeżeniu modelu. Użyj kolumny bezpośrednio, a obliczenia na wierszu zapisuj zwykłymi operatorami.
  5. Gdy transakcja ma znać sumę swojej grupy, policz ją raz w wymiarze i pobierz przez RELATED. Kolumna z ALLEXCEPT daje ten sam wynik, ale przejście kontekstu wykonuje dla każdej transakcji. Na modelu Nordvelli obie wersje są równe we wszystkich 108 675 wierszach, dla Gdańska to 27 854 861,19 zł. Wersja z RELATED działa obok kolumny z CALCULATE bez zależności cyklicznej.
    -- w FaktSprzedaz, przez wymiar (15 obliczeń w DimSklepy)
    Sprzedaż sklepu z wymiaru =
    RELATED ( DimSklepy[Sprzedaż sklepu] )
    
    -- w FaktSprzedaz, przez ALLEXCEPT (obliczenie w każdym wierszu)
    Sprzedaż sklepu w wierszu =
    CALCULATE (
        SUM ( FaktSprzedaz[WartoscNetto] ),
        ALLEXCEPT ( FaktSprzedaz, FaktSprzedaz[SklepID] )
    )
  6. Gdy wynik ma się tylko wyświetlać w wizualizacji, zrób z niego miarę zamiast kolumny. Miara liczy się w kontekście filtra wizualizacji, reaguje na fragmentatory i nie zajmuje pamięci w tabeli. Kiedy kolumna obliczeniowa wydłuża odświeżanie i czym ją zastąpić, piszemy w poście o kolumnie obliczeniowej, która spowalnia odświeżanie.

Jak sprawdzić, że problem zniknął

Sprawdź kolumnę na kilku wierszach i na sumie. Po poprawce wartości różnią się między sklepami, a ich suma zgadza się z miarą: na modelu Nordvelli SUMX(DimSklepy, CALCULATE(SUM(FaktSprzedaz[WartoscNetto]))) daje 467 775 009,17 zł, tyle co [Łączna sprzedaż], a kolumna z liczbą transakcji przez RELATEDTABLE sumuje się do 108 675, czyli do liczby wierszy FaktSprzedaz. Gdy wszystkie wiersze mają tę samą wartość albo suma kolumny jest wielokrotnością miary, w formule brakuje przejścia kontekstu.

Przy każdej kolumnie obliczeniowej z agregacją sprawdź dwie rzeczy: czy formuła ma CALCULATE albo RELATEDTABLE i czy tabela jest po stronie jeden relacji z faktami. W tabelach faktów trzymaj tylko kolumny liczone z własnego wiersza albo pobrane przez RELATED.

Źródła i sposób sprawdzenia

  • Microsoft Learn: CALCULATE function (DAX), przejście kontekstu wiersza w kontekst filtra (źródło)
  • Microsoft Learn: RELATEDTABLE function (DAX) (źródło)
  • Microsoft Learn: DAX overview (kontekst wiersza i kontekst filtra) (źródło)
  • Microsoft Learn: ALLEXCEPT function (DAX) (źródło)
  • Sprawdzone zapytaniami DAX na modelu Nordvelli: kolumny DimSklepy z SUM, CALCULATE i RELATEDTABLE, iteratory SUMX, FILTER, AVERAGEX i MAXX, kolumny FaktSprzedaz z CALCULATE, ALLEXCEPT i RELATED.

Zachowanie programu sprawdziliśmy w Power BI Desktop 2.157 (wydanie z sierpnia 2026 r.) z polskim interfejsem, a przykłady na modelu sieci Nordvella z bezpłatnego kursu Power BI. Microsoft wydaje nową wersję Power BI Desktop co miesiąc, więc nazwy przycisków i limity usługi mogą się zmieniać. Aktualne wartości podaje dokumentacja Microsoft Learn.

Wróć do listy: 131 najczęstszych pytań i problemów związanych z Power BI

Najczęściej zadawane pytania

Co to jest przejście kontekstu w DAX?
To zamiana bieżącego wiersza na kontekst filtra, którą wykonuje CALCULATE wywołane w kontekście wiersza, na przykład w kolumnie obliczeniowej albo w SUMX. Po przejściu agregacja widzi tylko dane powiązane z tym wierszem. Odwołanie do miary w kontekście wiersza wykonuje przejście kontekstu automatycznie.
Dlaczego kolumna obliczeniowa z SUM pokazuje w każdym wierszu tę samą wartość?
SUM widzi tylko kontekst filtra, a w kolumnie obliczeniowej ten kontekst jest pusty, więc liczy całą tabelę. Na modelu Nordvelli kolumna SUM(FaktSprzedaz[WartoscNetto]) w tabeli sklepów ma w każdym wierszu 467 775 009,17 zł. Owinięcie sumy w CALCULATE daje sprzedaż każdego sklepu.
Czym różni się SUMX z miarą od SUMX z SUM w DAX?
Miara w SUMX wykonuje przejście kontekstu, więc w każdym wierszu liczy wynik dla tego wiersza. SUM bez CALCULATE w każdym wierszu liczy całą tabelę. Na modelu Nordvelli SUMX po sklepach z miarą daje 467 775 009,17 zł, a z samym SUM 7 016 625 137,55 zł, piętnaście razy więcej.
Czy w tabeli faktów warto używać CALCULATE w kolumnie obliczeniowej?
Zwykle nie. Gdy wiersze mają unikatowy klucz, CALCULATE z sumą zwraca po prostu wartość wiersza, a przejście kontekstu wykonuje się dla każdego wiersza. Druga taka kolumna w tej samej tabeli daje zależność cykliczną. Sumę grupy policz w tabeli wymiaru i pobierz ją do faktów przez RELATED.

Komentarze (0)

Musisz być zalogowany by móc dodać komentarz. Zaloguj się przez Google

Brak komentarzy...