Rate this post

Rozmowa techniczna sprawdzająca znajomość SQL rzadko polega na recytowaniu definicji z podręcznika akademickiego. Rekruterzy i liderzy techniczni niemal natychmiast przechodzą do tablicy lub edytora współdzielonego, weryfikując umiejętność myślenia relacyjnego, wyłapywania pułapek w danych oraz optymalizacji logiki biznesowej. Presja czasu, stres i konieczność pisania kodu bez podpowiedzi środowiska IDE sprawiają, że nawet osoby regularnie korzystające z prostych poleceń gubią się przy elementarnych operacjach logicznych.

Przygotowanie do technicznej części rekrutacji wymaga opanowania konkretnych wzorców zapytań, które powtarzają się w większości zadań wstępnych. Niezależnie od tego, czy celem jest stanowisko młodszego programisty backendu, analityka danych czy inżyniera QA, zestaw testowanych mechanizmów pozostaje spójny. Obejmuje filtrowanie z uwzględnieniem braków danych, grupowanie, precyzyjne łączenie tabel, podzapytania oraz podstawowe funkcje okna. Poniższy przegląd rozkłada na czynniki pierwsze 15 zapytań, które stanowią fundament weryfikacji wiedzy kandydata na poziomie juniorskim i średnim.

Fundamenty selekcji i precyzyjne filtrowanie rekordów

Początkowy etap weryfikacji zazwyczaj sprawdza, czy kandydat rozumie fizyczny i logiczny porządek przetwarzania danych przez silnik bazodanowy. Praca na surowych tabelach wymaga nie tylko wskazania odpowiednich kolumn, lecz przede wszystkim świadomości, jak eliminować niepotrzebne wiersze i w jaki sposób zachowują się operatory w obliczu nietypowych wartości.

Zapytanie 1: Selekcja unikalnych wartości bez ukrytych duplikatów

Jedno z pierwszych zadań rozgrzewkowych polega zazwyczaj na wyciągnięciu listy unikalnych kategorii, miast lub statusów z tabeli transakcyjnej zawierającej miliony wpisów. Zadanie brzmi prosto: pobierz unikalne kombinacje działów i stanowisk z tabeli pracowników. Kluczem jest poprawne zastosowanie klauzuli DISTINCT oraz zrozumienie, że działa ona na całym zbiorze kolumn wymienionych po słowie kluczowym, a nie wyłącznie na pierwszej z nich.

SELECT DISTINCT 
    dzial_id, 
    stanowisko 
FROM pracownicy;

Silnik bazy danych po odczytaniu wierszy z tabeli pracownicy grupuje identyczne pary wartości dzial_id oraz stanowisko, zwracając wyłącznie pojedynczy rekord dla każdej unikalnej kombinacji. Na rozmowie rekruter często pyta: co się stanie, jeśli do zapytania dodamy klucz główny id? Odpowiedź musi być natychmiastowa: unikalność klucza głównego sprawi, że DISTINCT przestanie redukować wiersze, ponieważ każdy zwrócony wiersz z definicji będzie unikalny, co niepotrzebnie obciąży procesor bazy operacją sortowania lub haszowania w pamięci.

Zapytanie 2: Trójwartościowa logika i obsługa brakujących danych (NULL)

Brak zrozumienia logiki trójwartościowej (ang. three-valued logic: TRUE, FALSE, UNKNOWN) to najczęstsza przyczyna dyskwalifikacji kandydata na wczesnym etapie. Rekruter przedstawia tabelę klientów z kolumną telefon i prosi o pobranie wszystkich osób, które nie podały numeru, lub wręcz przeciwnie – tych, których numer różni się od zdefiniowanego szablonu.

SELECT 
    id, 
    imie, 
    nazwisko, 
    telefon 
FROM klienci 
WHERE telefon IS NULL 
   OR status != 'AKTYWNY';

Podstawowy błąd polega na próbie użycia operatora równości: WHERE telefon = NULL. W relacyjnych bazach danych wartość NULL oznacza brak informacji, stan nieokreślony, a nie pusty ciąg znaków czy zero. Dowolne porównanie z NULL przy użyciu tradycyjnych operatorów (=, !=, <>) zwraca wartość logiczną UNKNOWN, co w klauzuli WHERE jest traktowane tak samo jak FALSE – wiersz zostaje odrzucony. Rozwiązaniem jest wyłącznie dedykowany operator IS NULL lub IS NOT NULL, ewentualnie użycie funkcji standaryzującej, takiej jak COALESCE(telefon, 'BRAK').

Zapytanie 3: Filtrowanie zakresowe i wzorce tekstowe z paginacją

Systemy webowe i aplikacje biznesowe rzadko pobierają wszystkie rekordy naraz. Standardem jest stronicowanie (paginacja) połączone z wyszukiwaniem po fragmencie tekstu oraz precyzyjnym zawężeniem dat. Zadanie testowe często wymaga pobrania drugiej strony wyników dla zamówień złożonych w danym kwartale, których opis zawiera określone słowo kluczowe.

SELECT 
    id, 
    numer_zamowienia, 
    wartosc_brutto, 
    data_utworzenia 
FROM zamowienia 
WHERE data_utworzenia BETWEEN '2024-01-01' AND '2024-03-31 23:59:59'
  AND numer_zamowienia LIKE 'FV/%/2024'
ORDER BY data_utworzenia DESC
LIMIT 10 OFFSET 10;

W tym zapytaniu istotne są trzy elementy techniczne:

  • Klauzula BETWEEN jest domknięta obustronnie, co przy typach danych przechowujących czas (np. TIMESTAMP) może prowadzić do pominięcia rekordów z ostatnich ułamków sekund, jeśli granica końcowa nie zostanie precyzyjnie określona.
  • Symbol % w operatorze LIKE zastępuje dowolny ciąg znaków o dowolnej długości, natomiast symbol _ (podkreślnik) zastępuje dokładnie jeden znak. Rozpoczęcie wzorca od symbolu wieloznacznego (np. LIKE '%2024') uniemożliwia wykorzystanie standardowego indeksu B-drzewa.
  • Konstrukcja LIMIT 10 OFFSET 10 pozwala na pobranie rekordów od 11 do 20. Podczas rozmowy warto wspomnieć, że przy wielomilionowych zbiorach duży OFFSET staje się nieefektywny (ang. deep pagination problem), a optymalniejszym rozwiązaniem staje się paginacja kursorowa (ang. keyset pagination).

Agregacja danych i raportowanie wielowymiarowe

Zapytania agregujące sprawdzają umiejętność syntetyzowania rozproszonych danych transakcyjnych w zwięzłe metryki biznesowe. Rekruterzy weryfikują tutaj znajomość kolejności wykonywania operacji w silniku bazy oraz różnicę między filtrowaniem pojedynczych wierszy a filtrowaniem wyliczonych grup.

Zapytanie 4: Zliczanie rekordów – pułapka COUNT(*) kontra COUNT(kolumna)

Klasyczne pytanie rekrutacyjne dotyczy różnicy w działaniu funkcji zliczającej. Polecenie brzmi: określ całkowitą liczbę transakcji oraz liczbę transakcji, w których zarejestrowano identyfikator polecającego użytkownika.

SELECT 
    COUNT(*) AS wszystkie_transakcje,
    COUNT(kod_rabatowy) AS zrealizowane_z_kodem,
    COUNT(DISTINCT klient_id) AS unikalni_klienci
FROM transakcje;

Funkcja COUNT(*) z

licza wszystkie wiersze w tabeli lub grupie bez wyjątku, traktując wiersz jako całość, nawet jeśli wszystkie jego pojedyncze kolumny zawierałyby wartości puste. Z kolei COUNT(kolumna) ignoruje wartości NULL, co czyni ją idealnym narzędziem do weryfikacji kompletności danych w określonym polu. Połączenie COUNT(DISTINCT kolumna) wymusza dodatkową fazę deduplikacji przed agregacją, co przy braku dedykowanego indeksu może diametralnie zmienić plan wykonania zapytania na niekorzyść wydajności.

Zapytanie 5: Warunkowe filtrowanie zagregowanych danych – WHERE a HAVING

Kolejnym sprawdzianem jest zrozumienie cyklu życia zapytania w silniku bazy danych. Zadanie brzmi: znajdź klientów, którzy w bieżącym roku zrealizowali więcej niż pięć zamówień o łącznej wartości przekraczającej 1000 zł, biorąc pod uwagę wyłącznie zamówienia o statusie zrealizowanym.

SELECT 
    klient_id, 
    COUNT(id) AS liczba_zamowien, 
    SUM(wartosc_brutto) AS laczna_wartosc
FROM zamowienia
WHERE status = 'ZREALIZOWANE'
GROUP BY klient_id
HAVING COUNT(id) > 5 
   AND SUM(wartosc_brutto) > 1000;

Kluczowym elementem weryfikacji jest tutaj poprawne rozdzielenie warunków. Klauzula WHERE filtruje rekordy źródłowe przed utworzeniem grup – odrzuca te zamówienia, które nie mają statusu zrealizowanego, redukując wielkość zbioru trafiającego do pamięci podręcznej agregacji. Klauzula HAVING wchodzi do gry dopiero po przetworzeniu grup przez GROUP BY, weryfikując wyniki funkcji agregujących (COUNT, SUM). Próba wstawienia funkcji agregującej do WHERE zakończy się natychmiastowym błędem składniowym, co rekruterzy natychmiast odnotowują jako brak zrozumienia faz egzekucji polecenia SQL.

Zapytanie 6: Agregacja warunkowa za pomocą konstrukcji CASE WHEN

Zamiast wielokrotnego odpytywania bazy i łączenia wyników w aplikacji, doświadczony specjalista potrafi przekształcić wiersze w kolumny bezpośrednio na poziomie bazy. To tak zwane pivotowanie danych, często wymagane w raportach menedżerskich.

SQL dla początkujących: 15 zapytań, które warto umieć na rozmowie o pracę
Źródło: Pexels | Autor: Stanislav Kondratiev
SELECT 
    EXTRACT(YEAR FROM data_transakcji) AS rok,
    SUM(CASE WHEN typ = 'PRZYCHOD' THEN kwota ELSE 0 END) AS suma_przychodow,
    SUM(CASE WHEN typ = 'KOSZT' THEN kwota ELSE 0 END) AS suma_kosztow,
    SUM(CASE WHEN typ = 'PRZYCHOD' THEN kwota ELSE -kwota END) AS bilans_netto
FROM operacje_finansowe
GROUP BY EXTRACT(YEAR FROM data_transakcji)
ORDER BY rok DESC;

Agregacja warunkowa pozwala na jednokrotne przeskanowanie tabeli (ang. single table scan) i wyliczenie kilku odrębnych metryk biznesowych w ramach pojedynczego przebiegu. Jest to technika znacznie bardziej optymalna niż konstruowanie osobnych zapytań dla każdego typu operacji i łączenie ich za pomocą operacji JOIN.

Łączenie relacji i integralność powiązań (JOINs)

Relacyjna natura baz danych opiera się na łączeniu rozproszonych encji. Na rozmowie rekrutacyjnej pytania o złączenia sprawdzają nie tylko znajomość syntaksu, lecz przede wszystkim zdolność przewidywania zachowania silnika w przypadku niespójności kluczy obcych lub występowania relacji jeden-do-wielu.

Zapytanie 7: Wykrywanie rekordów osieroconych (LEFT JOIN z warunkiem antysymetrycznym)

Typowe zadanie diagnostyczne: wskaż klientów, którzy zarejestrowali konto w systemie, ale nigdy nie złożyli żadnego zamówienia. Zadanie to można zrealizować podzapytaniem, jednak rekruterzy preferują rozwiązanie oparte na złączeniu lewostronnym z uwagi na jego naturalną wydajność w większości silników.

SELECT 
    k.id, 
    k.email, 
    k.data_rejestracji
FROM klienci k
LEFT JOIN zamowienia z ON k.id = z.klient_id
WHERE z.id IS NULL;

Konstrukcja LEFT JOIN pobiera wszystkie rekordy z lewej tabeli (klienci) oraz dopasowuje do nich wiersze z prawej tabeli (zamowienia). Jeśli powiązanie nie istnieje, silnik wstawia wartości NULL we wszystkich kolumnach pochodzących z prawej tabeli. Warunek WHERE z.id IS NULL filtruje wynik, pozostawiając wyłącznie tych klientów, dla których klucz główny tabeli zamówień nie został odnaleziony. Z punktu widzenia optymalizatora zapytań jest to często transformowane do mechanizmu Hash Anti Join, który drastycznie skraca czas przeszukiwania dużych wolumenów danych.

Zapytanie 8: Samozłączenie w strukturach hierarchicznych (SELF JOIN)

Klasyczny schemat organizacyjny, w którym pracownik posiada przełożonego będącego innym pracownikiem w tej samej tabeli, to stały punkt programów rekrutacyjnych. Weryfikuje on umiejętność posługiwania się aliasami i traktowania tej samej fizycznej tabeli jako dwóch niezależnych logicznych bytów.

SELECT 
    p.imie || ' ' || p.nazwisko AS pracownik,
    COALESCE(m.imie || ' ' || m.nazwisko, 'BRAK (Zarząd)') AS przelozony
FROM pracownicy p
LEFT JOIN pracownicy m ON p.menedzer_id = m.id;

Użycie LEFT JOIN zamiast INNER JOIN ma tutaj fundamentalne znaczenie biznesowe. W przypadku zastosowania złączenia wewnętrznego, dyrektor generalny lub prezes zarządu (który nie posiada wpisu w kolumnie menedzer_id, gdyż ma tam NULL) zostałby całkowicie wycięty z zestawienia końcowego. Rekruterzy zwracają baczną uwagę na takie detale, gdyż pokazują one wrażliwość kandydata na utratę danych brzegowych.

Zapytanie 9: Iloczyn kartezjański w generowaniu macierzy raportowych (CROSS JOIN)

Iloczyn kartezjański bywa w programowaniu bazodanowym traktowany jako błąd początkujących, jednak w analityce jest potężnym narzędziem. Scenariusz rekrutacyjny: przygotuj strukturę raportu, która uwzględni każdy aktywny produkt w każdym istniejącym sklepie stacjonarnym, nawet jeśli w danym punkcie sprzedaż danego towaru wyniosła zero.

SELECT 
    s.nazwa_sklepu,
    p.nazwa_produktu,
    COALESCE(SUM(sprz.ilosc), 0) AS sprzedana_ilosc
FROM sklepy s
CROSS JOIN produkty p
LEFT JOIN sprzedaz sprz ON s.id = sprz.sklep_id 
                       AND p.id = sprz.produkt_id
                       AND sprz.data_sprzedazy >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY s.nazwa_sklepu, p.nazwa_produktu;

Kombinacja CROSS JOIN generuje pełną siatkę kombinacji (każdy sklep z każdym produktem), a następujący po niej LEFT JOIN zagnieżdża w tej siatce faktyczne transakcje. Zwróć uwagę na umieszczenie warunku daty bezpośrednio w klauzuli ON, a nie w WHERE – przeniesienie go do WHERE przekształciłoby złączenie lewostronne w wewnętrzne, niszcząc bazową siatkę produktów o zerowej sprzedaży.

Podzapytania, operacje zbiorowe i CTE

Wraz ze wzrostem złożoności logiki rośnie zapotrzebowanie na modularyzację zapytań. Na rozmowie rekrutacyjnej kandydat musi wykazać, że potrafi pisać kod czytelny, łatwy w utrzymaniu i wolny od nieefektywnych podzapytań wielokrotnie skanujących te same zasoby.

Zapytanie 10: Podzapytania skorelowane i klauzula EXISTS kontra IN

Zadanie: znajdź dostawców, którzy zrealizowali przynajmniej jedną dostawę towaru o wartości jednostkowej przekraczającej średnią cenę w danej kategorii towarowej.

SELECT d.id, d.nazwa_firmy
FROM dostawcy d
WHERE EXISTS (
    SELECT 1 
    FROM pozycje_dostaw pd
    JOIN towary t ON pd.towar_id = t.id
    WHERE pd.dostawca_id = d.id
      AND pd.cena_jednostkowa > (
          SELECT AVG(t2.cena_katalogowa) 
          FROM towary t2 
          WHERE t2.kategoria_id = t.kategoria_id
      )
);

Podzapytanie w klauzuli EXISTS jest skorelowane – odwołuje się bezpośrednio do identyfikatora d.id z zapytania zewnętrznego. Silnik bazodanowy wykonuje tu optymalizację typu short-circuit: zatrzymuje przeszukiwanie tabeli powiązanej w momencie znalezienia pierwszego pasującego rekordu dla danego dostawcy. W przeciwieństwie do operatora IN, EXISTS jest całkowicie odporny na występowanie wartości NULL w podzapytaniu i zazwyczaj generuje stabilniejszy plan wykonania.

Zapytanie 11: Wspólne wyrażenia tablicowe (CTE) dla czytelności i ponownego użycia danych

Głęboko zagnieżdżone podzapytania stają się koszmarem w utrzymaniu. Zastosowanie klauzuli WITH (Common Table Expressions) pozwala na logiczne podzielenie skomplikowanego przepływu na etapy przypominające zmienne tymczasowe.

WITH SrednieWydatkiDzialow AS (
    SELECT 
        dzial_id, 
        AVG(pensja) AS srednia_pensja_dzialu
    FROM pracownicy
    GROUP BY dzial_id
),
OdchyleniaPracownikow AS (
    SELECT 
        p.id,
        p.imie,

p.nazwisko,
        p.dzial_id,
        p.pensja,
        s.srednia_pensja_dzialu,
        ROUND(p.pensja - s.srednia_pensja_dzialu, 2) AS roznica
    FROM pracownicy p
    JOIN SrednieWydatkiDzialow s ON p.dzial_id = s.dzial_id
)
SELECT *
FROM OdchyleniaPracownikow
WHERE roznica > 1500
ORDER BY dzial_id, roznica DESC;

Kandydat, który potrafi przekształcić nieczytelny łańcuch podzapytań w przejrzyste CTE, natychmiast zyskuje w oczach zespołu inżynierskiego. Konstrukcja WITH pozwala optymalizatorowi potraktować poszczególne bloki jako zmaterializowane tymczasowe widoki lub wstrzyknąć je bezpośrednio do głównego planu wykonania (tzw. inline expansion), zachowując przy tym modułowość kodu i ułatwiając późniejsze testy jednostkowe poszczególnych warstw transformacji.

Funkcje analityczne i okna danych (Window Functions)

Dla rekruterów funkcje okna są ostateczną linią podziału między kimś, kto jedynie kojarzy podstawowy składniowy szkielet SQL, a kandydatem potrafiącym realnie rozwiązywać zaawansowane problemy analityczne bez uciekania się do powolnych złączeń kaskadowych.

Zapytanie 12: Rankingi i eliminacja remisów – ROW_NUMBER kontra DENSE_RANK

Klasyczny test rekrutacyjny: znajdź drugą najwyższą pensję w każdym dziale, uwzględniając możliwość, że kilku pracowników może zarabiać dokładnie tyle samo.

WITH RankingWynagrodzen AS (
    SELECT 
        dzial_id,
        imie,
        nazwisko,
        pensja,
        DENSE_RANK() OVER (
            PARTITION BY dzial_id 
            ORDER BY pensja DESC
        ) AS pozycja_placowa
    FROM pracownicy
)
SELECT 
    dzial_id,
    imie,
    nazwisko,
    pensja
FROM RankingWynagrodzen
WHERE pozycja_placowa = 2;

W tym scenariuszu kluczem jest świadomy wybór funkcji. ROW_NUMBER() przypisuje unikalny numer każdemu wierszowi bez względu na duplikaty wartości, co przy remisie na pierwszym miejscu zwróci losowego kandydata jako numer dwa, ignorując fakt, że obaj liderzy zarabiają tyle samo. RANK() przy remisie na pozycji pierwszej pominie numer dwa i kolejnej osobie przypisze pozycję trzecią. Wyłącznie DENSE_RANK() zachowuje ciągłość numeracji, gwarantując, że pensja bezpośrednio niższa od maksymalnej zawsze otrzyma etykietę dwa, niezależnie od liczby pracowników na szczycie tabeli.

Zapytanie 13: Agregaty kroczące i trendy czasowe (Running Total)

Zadanie biznesowe: oblicz skumulowaną sumę przychodów ze sprzedaży dzień po dniu w ujęciu każdego roku, aby zarząd mógł na bieżąco monitorować realizację planu budżetowego.

SELECT 
    data_transakcji,
    wartosc_sprzedazy,
    SUM(wartosc_sprzedazy) OVER (
        PARTITION BY EXTRACT(YEAR FROM data_transakcji)
        ORDER BY data_transakcji
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS skumulowany_przychod_ytd
FROM raporty_dzienne;

Definicja ramki okna za pomocą ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW precyzuje, jak silnik agreguje rekordy: sumuje wszystkie wiersze od początku danej partycji rocznej aż do bieżącego rekordu włącznie. W przeciwieństwie do tradycyjnego GROUP BY, funkcja okna nie zwija wierszy wynikowych – każdy dzień zachowuje swoją tożsamość w tabeli, zyskując jednocześnie kontekst historyczny wyliczony w ramach jednego przejścia po posortowanym indeksie.

Zapytanie 14: Analiza zmian okres-do-okresu z funkcjami LAG i LEAD

Scenariusz: dla każdego zamówienia klienta wyznacz czas, jaki upłynął od jego poprzedniego zakupu, oraz wskaż różnicę w koszyku zakupowym.

SELECT 
    klient_id,
    id AS zamowienie_id,
    data_zamowienia,
    wartosc_brutto,
    LAG(data_zamowienia, 1) OVER (
        PARTITION BY klient_id 
        ORDER BY data_zamowienia
    ) AS data_poprzedniego_zamowienia,
    wartosc_brutto - LAG(wartosc_brutto, 1, wartosc_brutto) OVER (
        PARTITION BY klient_id 
        ORDER BY data_zamowienia
    ) AS zmiana_koszyka
FROM zamowienia;

Funkcja LAG sięga do wiersza poprzedzającego w zdefiniowanym oknie bez konieczności kosztownego samozłączenia tabeli z przesunięciem dat. Trzeci argument funkcji – wartość domyślna – pozwala uniknąć wartości NULL dla pierwszego zamówienia w historii klienta, zastępując brakujący poprzedni koszyk wartością bieżącą, co daje zerową różnicę zamiast pustego pola. Na technicznym etapie rekrutacji taka dbałość o obsługę wartości brzegowych świadczy o dojrzałości projektowej.

Integralność danych i operacje modyfikujące

Zadania na rozmowach nie ograniczają się do bezpiecznego odczytu (SELECT). Inżynier danych lub programista backendowy musi radzić sobie z operacjami zmieniającymi stan bazy pod rygorem zachowania spójności transakcyjnej.

Zapytanie 15: Bezpieczna deduplikacja danych w tabeli produkcyjnej

Zadanie, które pojawia się regularnie: w tabeli logów zdarzeń z powodu błędu integracji pojawiły się zduplikowane rekordy o tym samym identyfikatorze transakcji i znaczniku czasu. Usuń duplikaty, zachowując wyłącznie jeden, najwcześniej wprowadzony rekord dla każdej grupy powtórzeń.

WITH DuplikatyDoUsuniecia AS (
    SELECT 
        id,
        ROW_NUMBER() OVER (
            PARTITION BY transakcja_id, kod_zdarzenia 
            ORDER BY utworzono_o ASC
        ) AS rnum
    FROM logi_systemowe
)
DELETE FROM logi_systemowe
WHERE id IN (
    SELECT id 
    FROM DuplikatyDoUsuniecia 
    WHERE rnum > 1
);

Zastosowanie CTE w połączeniu z funkcją ROW_NUMBER() izoluje logikę identyfikacji nadmiarowych wierszy od samej operacji fizycznego usuwania. Partycjonowanie po kluczach logicznych oraz sortowanie rosnące po dacie gwarantuje, że rekord oznaczony jako rnum = 1 to wpis pierwotny, a każdy z numerem wyższym to duplikat przeznaczony do usunięcia. Takie podejście eliminuje ryzyko przypadkowego wyczyszczenia unikalnych danych i jest w pełni deterministyczne, co na rozmowie kwalifikacyjnej stanowi ostateczny dowód na to, że kandydat potrafi operować na systemach produkcyjnych bez narażania biznesu na straty.

Opanowanie tych piętnastu schematów pozwala swobodnie nawigować po niemal każdym zadaniu algorytmicznym opartym na SQL. Rekruterzy nie szukają pamięciowego opanowania setek funkcji wbudowanych w konkretne dialekty – sprawdzają przede wszystkim instynkt relacyjny, umiejętność przewidywania wolumenu przetwarzanych danych oraz zdolność pisania zapytań, które zachowują spójność logiczną w obliczu niekompletnych lub zduplikowanych rekordów.

Poprzedni artykułGPL w aplikacji komercyjnej: kiedy trzeba udostępnić kod źródłowy?
Marta Rutkowski
Marta Rutkowski zajmuje się tematami AI/ML i analizą danych, łącząc podejście inżynierskie z krytycznym spojrzeniem na trendy. W tutorialach prowadzi od przygotowania danych po wdrożenie, pokazując, jak mierzyć jakość modeli, unikać przecieków danych i kontrolować koszty obliczeń. Zamiast obietnic „magii AI” stawia na powtarzalne eksperymenty, czytelne metryki i rzetelne źródła, w tym publikacje oraz dokumentację narzędzi. Interesują ją także kwestie etyki, prywatności i bezpieczeństwa w systemach uczących się, dlatego wnioski zawsze osadza w realnych ograniczeniach.