Wszystkie artykuły

KajoData / Praca i rozwój

Jak działa Power Query: od surowego pliku do powtarzalnego procesu

Power Query zapamiętuje sposób przygotowania danych. Pokazuję, co dokładnie powtarza, jak sprawdzić wynik i dlaczego nowy plik nadal może wymagać poprawienia reguł.

Kajo RudzińskiOpublikowano Aktualizacja 11 min czytania

Warsztat porządkujący geometryczne elementy w kolejnych etapach, z osobną kontrolą niepasującego elementu.

Jeżeli co miesiąc poprawiasz podobny plik, prawdopodobnie masz już w głowie przepis: usuń spacje, ustaw daty, połącz kanały sprzedaży i sprawdź sumę. Power Query pozwala ten przepis zapisać. Przy kolejnym odświeżeniu wykonujesz tę samą logikę na nowych danych. Zamiast pamiętać serię ręcznych poprawek, możesz ją obejrzeć, nazwać i sprawdzić.

Lubię tę możliwość szczególnie przy powtarzalnych raportach. Nie oznacza ona jednak, że narzędzie rozumie biznes albo samo rozpozna każdą zmianę pliku. Jeśli ktoś zmieni nazwę kolumny lub zacznie zapisywać kwotę w innej jednostce, wcześniejsza logika może przestać pasować. Automatyzacja powtarza także błędne założenia. Właśnie dlatego kontrola wyniku jest częścią przepisu.

Pokażę to na czterech syntetycznych rekordach. Przykład M można wkleić do pustego zapytania w Excelu dla Windows lub Power BI Desktop. Sprawdzony został w silniku Power Query Excela. Nie wymaga pliku firmy, połączenia z bazą ani credentiali. Dzięki temu najpierw poznasz transformacje, a dopiero później dołożysz własne źródło.

Gdzie Power Query znajduje się w pracy analityka

Power Query służy do pobierania i przekształcania danych. Działa między innymi w Excelu oraz produktach Power BI, z różnicami zależnymi od środowiska. Nie jest silnikiem stojącym za każdym narzędziem analitycznym. Python i Tableau mogą przygotowywać dane własnymi mechanizmami. Opis Microsoftu wskazuje produkty korzystające z Power Query oraz rozróżnia jego wersje desktopowe i przeglądarkowe.

Myślę o pracy w trzech częściach: źródło, transformacje, miejsce wykorzystania wyniku. Źródłem może być plik albo dostępny konektor do systemu. Transformacje opisują reguły przygotowania. Wynik trafia tam, gdzie pozwala aplikacja, na przykład do tabeli arkusza albo modelu danych. Dostępność konektorów, sposobów uwierzytelnienia i ładowania sprawdzam dla konkretnego produktu oraz wersji.

Przygotowanie tabel nie jest całym modelowaniem. Relacje między tabelami i miary DAX wymagają osobnych decyzji. Dokumentacja relacji Power BI opisuje ich rolę w przekazywaniu filtrów. Scalanie tabel w Power Query zmienia przygotowywany wynik, podczas gdy relacja modelu łączy sposób analizowania już załadowanych tabel. Te operacje mogą dawać różne konsekwencje.

Zaczynam od znaczenia kolumn

W naszym przykładzie jeden wiersz reprezentuje jedną umowną transakcję. ID jest tekstowym identyfikatorem, Channel kanałem, Date datą, a Amount kwotą. Zachowuję początkowe zera w ID. Numer 001 ma identyfikować rekord, a nie służyć do dodawania. Zamiana go na liczbę tylko dlatego, że wygląda liczbowo, byłaby niepotrzebną utratą informacji.

Kanały mają różne wielkości liter i zbędne spacje. Daty zapisano zgodnie z polskim porządkiem, a kwoty używają przecinka dziesiętnego. Jedna kwota jest nieznana, jedna równa zero, jedna ujemna. Ujemną traktuję w ćwiczeniu jako korektę, więc pozostaje w sumie. Nie usuwam jej automatycznie jako błędu. W rzeczywistym źródle znaczenie takiej wartości trzeba potwierdzić.

Przed transformacją zapisuję oczekiwania: cztery wiersze, cztery zachowane identyfikatory, trzy znane kwoty i jedna brakująca. Po uporządkowaniu zostaną dwa kanały: WEB oraz PARTNER. Suma dostępnych kwot powinna wynosić 100,50. Te informacje pozwalają sprawdzić rezultat niezależnie od tego, czy edytor pokazuje zielony komunikat i poprawnie wyglądającą tabelę.

Uruchamiam kompletny przykład M

Utwórz puste zapytanie, otwórz Edytor zaawansowany i zastąp zawartość poniższym kodem. Nazwij zapytanie OrdersPrepared, ponieważ kolejny przykład będzie się do niego odwoływać. Nazwy poleceń w interfejsie mogą się różnić językiem i wersją, ale kod M pozostaje taki sam w obu wersjach tego artykułu. To samodzielne źródło zapisane wewnątrz zapytania.

let
    Source = #table(
        type table [ID = text, Channel = text, Date = text, Amount = nullable text],
        {
            {"001", " Web ", "31.01.2026", "120,50"},
            {"002", "web", "01.02.2026", "0,00"},
            {"003", "Partner", "02.02.2026", null},
            {"004", "partner", "03.02.2026", "-20,00"}
        }
    ),
    CleanChannel = Table.TransformColumns(Source, {{"Channel", each Text.Upper(Text.Trim(_)), type text}}),
    Typed = Table.TransformColumnTypes(CleanChannel, {{"Date", type date}, {"Amount", Currency.Type}}, "pl-PL")
in
    Typed

Source tworzy tabelę wejściową. CleanChannel usuwa skrajne spacje i ujednolica wielkość liter w kanale. Typed zmienia typ daty i kwoty, jawnie wskazując kulturę pl-PL. Po słowie in wybieram wynik zapytania. Nazwy kroków są moimi nazwami, a nie obowiązkowym zestawem poleceń. Mogę je zmienić, jeśli zachowam prawidłowe odwołania pomiędzy nimi.

W edytorze wiele takich kroków da się zbudować przez interfejs. Kod pozwala zobaczyć dokładną regułę i jej parametry. Czytam go od źródła do wyniku, śledząc zależności, ale nie zakładam, że lista kroków jest dosłownym zapisem fizycznego wykonania silnika. Dla pierwszego ćwiczenia najważniejsze jest zrozumienie, jaki wynik przekazuję do następnej transformacji.

Typ danych i kultura decydują o interpretacji

Kwota 120,50 ma zostać liczbą sto dwadzieścia i pół. Data 31.01.2026 ma oznaczać ostatni dzień stycznia. Parametr kultury w Table.TransformColumnTypes pomaga jawnie ustalić interpretację tekstu. Nie polegam na tym, że komputer autora i komputer odbiorcy mają identyczne ustawienia regionalne. Na granicy między tekstem i liczbą takie założenie bywa kosztowne.

Currency.Type oznacza tutaj typ liczby o stałej precyzji dziesiętnej, przydatny dla naszego przykładu kwot. Nie przypisuje wartości automatycznie do konkretnej waluty ani nie przelicza kursów. Jednostka nadal jest założeniem danych. Z kolei zmiana wyglądu komórki w Excelu nie naprawi błędnej interpretacji, która nastąpiła wcześniej w zapytaniu. Format wyświetlania i typ wartości rozwiązują inne zadania.

Automatyczne rozpoznawanie typów traktuję jako propozycję do kontroli. Szczególnie oglądam identyfikatory, daty i wartości z separatorami. Dokumentacja typów Power Query wyjaśnia zależność od ustawień regionalnych. W próbce dodaję datę z dniem większym od dwunastu, żeby nie opierać testu wyłącznie na wartościach, które można odczytać na dwa sposoby.

Sprawdzam wynik osobnym zapytaniem

Po przygotowaniu tabeli utwórz drugie puste zapytanie. Poniższy kod odwołuje się do OrdersPrepared, więc wykonaj go dopiero po nazwaniu pierwszego zapytania. Wynik kontrolny ma jeden wiersz i trzy kolumny. Nie trzeba kopiować całej logiki transformacji, aby ją podsumować; odwołanie pozwala korzystać z tego samego przygotowanego zbioru.

let
    Source = OrdersPrepared,
    Rows = Table.RowCount(Source),
    KnownAmounts = List.NonNullCount(Source[Amount]),
    KnownTotal = List.Sum(Source[Amount])
in
    #table({"Rows", "KnownAmounts", "KnownTotal"}, {{Rows, KnownAmounts, KnownTotal}})

Otrzymuję 4, 3 oraz 100,5. List.NonNullCount liczy znane wartości, a List.Sum sumuje wartości różne od null. Dlatego brakująca kwota nie staje się zerem. Gdy nie ma żadnej znanej wartości, ta suma zwraca null. W raporcie zachowuję obok niej liczbę dostępnych obserwacji, żeby odbiorca widział zakres obliczenia.

Patrzę również na wynik wiersz po wierszu: 001 ma WEB i 120,5; 002 ma WEB i zero; 003 ma PARTNER i null; 004 ma PARTNER i minus dwadzieścia. Daty powinny mieć właściwy rok, miesiąc i dzień. Samo uzgodnienie sumy nie wykryłoby zamiany dwóch identyfikatorów ani przesunięcia dat. Kilka różnych kontroli pokrywa różne rodzaje pomyłek.

Błąd konwersji jest informacją

W kopii ćwiczenia zastąp tekst 120,50 słowem oops. Próba nadania typu liczbowego powinna ujawnić błąd w komórce. Nie zamieniam go od razu na zero. Najpierw chcę wiedzieć, czy źródło zawiera literówkę, nowy format czy wartość o innym znaczeniu. Opis obsługi błędów rozróżnia problemy całego kroku oraz pojedynczych wartości.

Trzecie zapytanie może policzyć wiersze z błędem kwoty. Na oryginalnej próbce zwróci zero, po opisanej zmianie jeden. To mały raport diagnostyczny, który nie usuwa problematycznego rekordu. Table.SelectRowsWithErrors pozwala ograniczyć sprawdzanie do wskazanych kolumn. Dzięki temu wiem, czego dotyczy licznik, i nie mylę go z ogólną oceną całej jakości danych.

let
    ErrorRows = Table.SelectRowsWithErrors(OrdersPrepared, {"Amount"})
in
    #table({"AmountErrorRows"}, {{Table.RowCount(ErrorRows)}})

Usuwanie wierszy z błędami może być uzasadnioną decyzją, ale zmienia zbiór analizy. Zapisuję więc liczbę odrzuceń oraz powód. Gdy błąd dotyczy ważnej kwoty, ciche pominięcie rekordu może zaniżyć sumę. Wolę widoczny wyjątek wymagający rozstrzygnięcia niż pozornie kompletny raport, którego wyniku nie potrafię uzgodnić ze źródłem.

Odświeżenie powtarza regułę na aktualnym źródle

W naszym kodzie źródło jest wpisane na stałe, więc samo odświeżenie odtworzy te same cztery rekordy. Żeby zobaczyć zmianę, dopisz w Source nowy wiersz albo zmień dane istniejącego. Przy pliku zewnętrznym zapytanie ponownie odczytuje wskazane źródło zgodnie ze swoją definicją. Nadal musisz wiedzieć, czy pobiera jeden konkretny plik, wszystkie pasujące pliki czy dane z systemu.

Nie wpisuję ręcznych korekt do tabeli wynikowej jako sposobu naprawy procesu. Kolejne ładowanie może je zastąpić. Poprawiam regułę, źródło albo przygotowuję osobną, kontrolowaną tabelę korekt. Dzięki temu potrafię odtworzyć wynik i wyjaśnić, skąd wzięła się zmiana. Ręczna poprawka w niewłaściwym miejscu łatwo staje się wiedzą dostępną tylko jednej osobie.

Jeżeli ktoś zmieni nagłówek Amount na Value, nasz krok odwołujący się do Amount wymaga dostosowania. Dodanie nowego wiersza i zmiana schematu to różne sytuacje. Nie obiecuję, że raz napisane zapytanie będzie działać dla dowolnego przyszłego pliku. Ustalam oczekiwane kolumny oraz sygnał, po którym proces ma zatrzymać się do kontroli.

Modułowy most z elementem, który nie pasuje do istniejącego połączenia, oglądanym przez lupę.

Dołączanie i scalanie odpowiadają na różne potrzeby

Gdy mam dwa pliki z transakcjami z kolejnych miesięcy, zwykle chcę dołożyć wiersze. Służy temu append, czyli dołączanie. Dokumentacja dołączania wskazuje dopasowanie po nazwach kolumn. Jeżeli jeden plik używa Amount, a drugi Value, mogę dostać dwie kolumny z brakami zamiast jednej spójnej kwoty. Najpierw ustalam wspólną strukturę.

Gdy chcę dopisać opis kanału z osobnego słownika, potrzebuję merge, czyli scalania po kluczu. Opis scalania pokazuje dostępne rodzaje połączeń. Kontroluję, czy klucz w słowniku jest unikalny. Dwa pasujące opisy mogą po rozwinięciu zwiększyć liczbę wierszy, a potem sumę kwot. Poprawnie wykonana operacja nie gwarantuje poprawnej relacji danych.

W folderze miesięcznych plików sprawdzam także nazwy, rozszerzenia i zakres okresów. Kopia tego samego pliku może podwoić transakcje. Plik tymczasowy może mieć inną strukturę. Sam fakt, że wszystkie pliki znajdują się w jednym folderze, nie czyni ich poprawnym zbiorem do połączenia. Zapisuję regułę wyboru i listę plików użytych w konkretnym odświeżeniu.

Podgląd nie zastępuje kontroli pełnego zbioru

Profilowanie kolumn pomaga znaleźć braki, błędy i nietypowe wartości. Sprawdzam jednak, czy oglądam próbkę, czy cały zbiór. Narzędzia profilowania mogą domyślnie pracować na pierwszym tysiącu wierszy. Poprawny początek pliku nie dowodzi, że pod koniec nie pojawiła się inna data albo błędna kwota. Zakres kontroli powinien być świadomym wyborem.

Na większych danych zapisuję liczebności przed i po filtrach, liczbę brakujących kluczy oraz sumę znanych kwot. Nie oczekuję, że każda transformacja zachowa liczbę wierszy. Filtr ma ją zmniejszyć, a grupowanie zmienić poziom szczegółowości. Chcę natomiast umieć wyjaśnić każdą zmianę. Jeżeli nie potrafię, wracam do pierwszego kroku, na którym wynik odbiegł od oczekiwań.

Usuwanie duplikatów również wymaga definicji. Dwa identyczne wiersze mogą być powtórzonym importem albo dwoma rzeczywistymi zdarzeniami. Nie używam tej operacji jako uniwersalnego sprzątania. Ustalam klucz, zakres oraz regułę wyboru rekordu, gdy pozostałe pola się różnią. Więcej o takich decyzjach pokazuję w artykule o czyszczeniu danych.

Wydajność i ładowanie mają swoje granice

Przy części źródeł Power Query może przekazać fragment przekształceń do źródła, co opisuje mechanizm query folding. Zależy to od konektora i operacji. Nie zakładam, że każdy krok wykona się lokalnie ani że każdy zostanie przekazany do bazy. Mały przykład z tabelą w kodzie sprawdza logikę, bez pomiaru wydajności dużego procesu.

Ładowanie do modelu danych omija konieczność umieszczania wszystkich rekordów w arkuszu, ale nie daje nieskończonej pojemności. Pamięć, model, środowisko i źródło nadal ograniczają pracę. Wybieram miejsce ładowania według sposobu użycia wyniku i dostępnych możliwości produktu. Nie kopiuję miliona rekordów na arkusz tylko po to, żeby później pokazać podsumowanie kilkunastu kategorii.

Harmonogram odświeżania i dostęp do źródeł zależą od aplikacji oraz środowiska. Plik działający na komputerze autora nie musi odświeżyć się bez dodatkowej konfiguracji na innym komputerze lub w usłudze. Przy przekazaniu procesu dokumentuję źródło, właściciela i warunki odświeżenia. Sekretów nie wpisuję do opisu zapytania ani publicznego przykładu.

Przenoszę ćwiczenie na własny plik

Kiedy rozumiesz przykład, zastąp tabelę wpisaną w kodzie połączeniem ze źródłem. Najpierw obejrzyj wynik samego kroku Source, zanim dołożysz czyszczenie. Plik może zawierać dodatkowy wiersz tytułu, kilka arkuszy albo komentarz pod tabelą. Nie zakładam, że dane zaczynają się w pierwszej widocznej komórce. Wybieram właściwy obiekt i sprawdzam jego nagłówki oraz liczbę rekordów.

Potem odtwarzam reguły pojedynczo. Po zmianie kanału oglądam listę wartości, po zmianie typów błędy, a po filtrze liczebność. Jeżeli kolumny nazywają się inaczej, dopasowuję odwołania świadomie. Nie wklejam kodu z artykułu do dowolnej tabeli z oczekiwaniem, że rozpozna właściwe pola po ich wyglądzie. Nazwy, typy i znaczenie kolumn tworzą umowę pomiędzy źródłem a zapytaniem.

Na pierwsze uruchomienie wybieram kopię małego, reprezentatywnego pliku. Zawiera ona także wyjątki, a nie tylko najładniejsze wiersze. Zachowuję wynik kontrolny, aby porównać kolejne odświeżenie. Gdy proces przechodzi na nowe źródło, sprawdzam go ponownie nawet wtedy, gdy interfejs pokazuje identyczne nagłówki. Taka sama struktura nie dowodzi, że pola nadal opisują te same zdarzenia i jednostki.

Co sprawdzić przed użyciem w raporcie

Wykonaj dwa doświadczenia. Najpierw dodaj rekord 005, kanał web, datę 04.02.2026 i kwotę 30,00. Kontrola powinna pokazać pięć wierszy, cztery znane kwoty i sumę 130,50. Potem wróć do oryginału i wstaw oops w pierwszej kwocie. Raport błędów powinien wskazać jeden wiersz. To dwa różne rodzaje zmiany i dwie różne oczekiwane reakcje.

Sprawdź również pusty wynik: zero wierszy, zero znanych kwot i null sumy. Zdecyduj, jak Twój raport ma go opisać. Pusta dostawa pliku może oznaczać brak zdarzeń albo problem procesu, więc nie nadaję temu stanowi znaczenia bez dodatkowej informacji. Podobnie zachowuję zerową kwotę jako liczbę, zamiast wrzucać ją do tej samej kategorii co brak.

Na koniec opisz każdy krok prostym zdaniem i zapisz oczekiwane kontrole. Jeśli korzystasz z AI do pomocy przy M, sprawdź wygenerowany kod na tej samej próbce i przypadkach brzegowych. Działający podgląd jest początkiem weryfikacji. Powtarzalny proces powinien dać się odświeżyć, porównać z oczekiwaniem i przekazać drugiej osobie bez odtwarzania Twoich ruchów z pamięci.

Film o Power Query

Poniższy film jest przypisany do polskiego źródła artykułu. Przykład M w tekście to osobne ćwiczenie z syntetycznymi danymi, które możesz wykonać bez pobierania dodatkowych plików.

Najważniejsze w pigułce

Czy muszę znać język M, żeby zacząć?

Nie. Wiele operacji wykonasz w edytorze, który zapisuje odpowiadający im kod M. Czytanie kodu pomaga później zrozumieć zależności i poprawić bardziej złożone kroki.

Czy odświeżenie naprawi każdą zmianę pliku źródłowego?

Nie. Odświeżenie ponownie wykonuje zapisaną logikę. Zmiana nazwy kolumny, formatu daty lub znaczenia danych może wymagać zmiany zapytania i kontroli wyniku.

Czy Power Query tworzy relacje i miary DAX?

Przygotowuje tabele do użycia w modelu. Relacje modelu oraz miary DAX to osobne elementy pracy w Power BI lub odpowiednich funkcjach Excela.

Przy pracy nad artykułem użyto w zautomatyzowany sposób sztucznej inteligencji. Jeśli też chcesz tak umieć, sprawdź ten projekt.

Kajo Rudziński

Newsletter KajoData

Zostańmy w kontakcie.

Piszę o analizie danych, uczeniu się i zmianie pracy. Dzielę się tym, co pomaga mi ogarniać technologię i zawodowe decyzje. Jeśli lubisz ten sposób myślenia, zapraszam do mojego newslettera.