Projekt NBP w Fabric migracja do Databricks

  • Projekt NBP w Fabric migracja do Databricks

    Posted by Klaudia Kuszczak on 2026-08-09 at 09:21

    Cześć! Niedawno obroniłam pracę inżynierską „Projekt i implementacja systemu przetwarzania danych opartego o architekturę Lakehouse w Microsoft Fabric” i chcę podzielić się z wami projektem, który był jej największym elementem. Przy okazji dziękuję Markowi Czumie za solidną dawkę wiedzy o Sparku. Przydała się i od strony teorii i praktyki.

    https://github.com/klaudia-q/nbp-lakehouse-fabric

    W wielu projektach technologia zmienia się z różnych powodów. U mnie prozaicznie: wygaśnie dostęp do konta uczelnianego, a razem z nim do Fabrica. Uznałam to za dobrą okazję, żeby spróbować sił w migracji i przenieść projekt na Databricks, równolegle ucząc się z kursu Krzysztofa Nojmana.

    Im dłużej patrzę na własny kod, tym wyraźniej widzę, że parę rzeczy działa u mnie wyłącznie dlatego, że przy każdym uruchomieniu nadpisuję wszystko overwrite. Jak wejdzie MERGE, to się posypie.

    Najbardziej nie daje mi spokoju klucz surogatowy w wymiarze walut. Nadaję go row_number()em po kodzie waluty, od nowa przy każdym przebiegu. Teraz jest spójnie, bo fakty przeliczam w tym samym locie. Ale jak przejdę na przyrostowe i dojdzie nowa waluta gdzieś w środku alfabetu, numeracja się przesunie i fakty zapisane wcześniej zaczną wskazywać nie na to, co trzeba. Widzę trzy wyjścia i przy każdym mam wątpliwość. Mogę zrobić MERGE na wymiarze, żeby klucz dostawały tylko nowe kody, a stare zostawały nietknięte poprawnie, ale wprowadza stan, który trzeba pilnować. Mogę liczyć klucz deterministycznie, sha2(currency_code, 256) problem znika bez żadnego stanu, tylko dostaję nieczytelny string zamiast małego inta w tabeli faktów. Albo mogę w ogóle odpuścić surogat i zostawić sam currency_code: wymiar ma kilkadziesiąt wierszy, żadnej historii tam nie wersjonuję, a kod ISO jest stabilny.
    Ciągnie mnie do trzeciego, bo najprostsze, ale mam z tyłu głowy, że gdybym kiedyś chciała SCD2, sama sobie zamykam drogę. Robicie tak u siebie przy małych wymiarach, czy to jednak grzech?
    Druga rzecz, na której straciłam najwięcej czasu przy pisaniu: Bronze zapisywał przez .save(„Tables/…”), a Silver czytał przez nazwę tabeli. Pliki leżały gdzie trzeba, tylko Spark ich nie widział w katalogu. CREATE TABLE … LOCATION ze ścieżką względną nie pomogło, dopiero pełna abfss://. Zrzuty z całego grzebania wrzuciłam do repo, bo szkoda było wyrzucać. W Databricks pod Unity Catalog to pewnie w ogóle nie wystąpi, ale jestem ciekawa, czy ktoś świadomie trzyma zapis po ścieżce zamiast saveAsTable i ma ku temu powód, którego nie widzę.

    No i trzecia, bardziej modelowa. Łączę A z C full outer joinem, bo teoretycznie daty mogą się rozjechać. W praktyce na moich danych nie trafiłam na ani jeden wiersz bez pary, więc to jest inner udający coś więcej. Tu też mam dwie drogi: zostawić full outer i dorzucić coalesce() na nazwę waluty, albo zejść do innera i wywalać przypadki bez pary do logu jako anomalię. Druga wydaje mi się uczciwsza wobec danych, ale zakłada, że rozjazd między A i C to błąd, a nie dopuszczalny stan, czego szczerze mówiąc nie sprawdziłam w dokumentacji NBP.

    Jak ktoś przechodził już drogę Fabric -> Databricks, to chętnie posłucham, co sprawiło wam problem?

    Marek Czuma replied 2 tygodnie, 5 dni temu 3 Members · 4 Replies
  • 4 Replies
  • Przemysław Lelewski

    Member
    2026-08-09 at 15:33
    939 Exp

    Cześć

    a propos pierwszego problemu z „surrogate key” : mam sporo do czynienia na obecnym projekcie, ale składa się na nie kilka kolumn i dla nich nadawany jest klucz (integer). Z tego co widzę u Ciebie jest 1:1 czyli waluta:klucz. To jaki jest cel tworzenia takiego klucza i nie pozostawianie/wstawianie po prostu kodu waluty? (sorki, jeśli coś pomieszałem – jeśli chodzi o Sparka to jestem dopiero po kursie Marka i zwyczajnie nie zaczaiłem zbytnio kodu).

  • Marek Czuma

    Organizer
    2026-08-10 at 10:20
    5394 Exp

    Przede wszystkim – gratulacje obronionej inżynierki! I dobry pomysł z migracją projektu na DBX;-).

    Jeśli chodzi o klucz – to jaki jest cel nadawania czegoś jeszcze poza walutą? Jeśli masz kolumnę unikatową, to po prostu to może być jej kluczem:-). Dodam jeszcze, że bardzo dużo zależy tutaj od decyzji biznesowej oraz logiki systemu, bo musisz sobie odpowiedzieć na pytanie: „czy w dalszej części systemu zależy mi na tym, żeby wiedzieć, które wiersze są nowe?”. Jeśli tak, to można robić to przyrostowo poprzez merge. W Databricks możesz też wykorzystać mechanizm CDF. Jeśli nie – to tabelę która ma kilkadziesiąt wierszy WARTO (moim zdaniem) nadpisywać, bo kod jest znacznie prostszy.

    Na drugi problem nie jestem w stanie odpowiedzieć niestety, bo nie znam fabric :(. W Databricks do danych z ADLSa masz dostęp poprzez Volume i to Volume podpinasz do ADLS, więc sam kod pozostaje niezmienny – i pobierasz parquety normalnie, a jak kiedyś zmienisz środowisko to Volume po prostu zmienia źródło, kod się nie zmienia.

    Trzeci problem zależy od decyzji i logiki biznesowej. Tak jak napisałaś – zależy co jest błędem. Jeśli coś może być błędem, to CHCESZ O TYM WIEDZIEĆ – i warto zrobić tak jak napisałaś, wywalać do tabeli błędów, a sam job dalej leci z tematem.

  • Klaudia Kuszczak

    Member
    2026-08-17 at 21:00
    926 Exp

    Cześć, dzięki za feedback do projektu. Bardzo go doceniam.❤

    Przemku, masz rację co do relacji: u mnie jest 1:1, więc nie ma czego sklejać z kilku kolumn. Mój jedyny argument za surogatem nie wynika z zasad modelowania, lecz z warstwy serwującej. Model trafia do Power BI, a tam w VertiPaq relacje po intach są tańsze niż po tekście. Przy Direct Lake nie jestem pewna, ile z tego zysku zostaje, więc zamiast bronić tego teoretycznie, po prostu zmierzę obie wersje po migracji.

    Marku, dzięki za przestawienie tego na pytanie o wymaganie biznesowe zamiast o poprawność modelu. Odpowiedź brzmi „nie”. Nic dalej nie potrzebuje wiedzieć, które wiersze wymiaru są nowe. Nadpisywanie kilkudziesięciu wierszy zostaje, a CDF zapisuję sobie na moment, gdy faktycznie pojawi się taka potrzeba.

    Za swój faktyczny błąd uznaję nie samo użycie klucza surogatowego, tylko sposób jego generowania. row_number() daje wartość zależną od zawartości tabeli w danym przebiegu, więc przy ładowaniu przyrostowym klucze by się rozjechały. Jeśli surogat zostanie, to tylko liczony deterministycznie, tak jak zrobiłam to od początku w wymiarze daty, gdzie klucz jest wyprowadzony bezpośrednio z wartości.

    Po przemyśleniach schodzę do INNER JOIN-a, a wiersze bez pary będą lądowały w osobnej tabeli błędów i dzięki temu job poleci dalej, a anomalia będzie policzalna, zamiast rozpływać się w NULL-ach.

    Podpowiedź o Volumes jest dla mnie najciekawsza, bo zdejmuje cały problem, z którym walczyłam w Fabricu i zamiast pilnować ścieżek w kodzie, przepina się źródło pod stałą nazwą. To realnie upraszcza plan migracji.

    Wrócę z wynikami, jak przepiszę projekt na Databricks.

    😀

    • Marek Czuma

      Organizer
      2026-08-19 at 07:14
      5394 Exp

      Super Klaudia, dobra robota! Zderzasz się z tymi prawdziwymi projektowymi projektami i myśleniem projektowym, a nie „tutorialowym”. To ci zaprocentuje;-). Powodzenia i dawaj znać o postępach!

Zaloguj się aby odpowiedzieć