Podstawy SQL dla analityka systemowego — SELECT, WHERE i pierwsza analiza danych
Podstawy SQL zaczynają się od pytania, nie od zapamiętywania słów kluczowych. Analityk pyta: jaki obiekt sprawdzam, jaki stan jest oczekiwany i jakie dane mogą to potwierdzić?
Wspólnym przykładem jest system zamówień. Zamiast tworzyć przypadkowe tabele, będziemy wracać do customers, orders, order_items, payments i historii statusów.
Zaczynamy od pytania, nie od składni
Pytanie brzmi: „Pokaż zamówienie 1002 i jego aktualny stan”. Dopiero potem powstaje zapytanie:
SELECT
order_id,
customer_id,
order_status,
payment_status,
external_order_id,
created_at,
updated_at
FROM orders
WHERE order_id = 1002;
1002 | 17 | CANCELLED | PAID | EXT-1002 | 2026-07-04 10:01 | 10:06Wynik mówi, co zapisano dla wskazanego rekordu. Nie mówi jeszcze, czy status jest zgodny z wymaganiem ani dlaczego powstał.
SELECT i FROM — co oraz skąd czytamy
SELECT wskazuje informacje, które chcemy otrzymać, a FROM źródło danych. SELECT * bywa użyteczny podczas szybkiego rozpoznania nieznanej tabeli, ale w trwałym zapytaniu lepiej wymienić kolumny jawnie. Wtedy intencja analizy jest widoczna, a zmiana tabeli nie rozszerza przypadkiem wyniku.
SELECT
o.order_id,
o.order_status,
o.payment_status
FROM orders AS o;
Wynik zwraca trzy świadomie wybrane kolumny dla każdego zamówienia. Alias o nie zmienia danych; upraszcza zapis i staje się ważny, gdy kolejne tabele mają kolumny o tych samych nazwach.
1001–1004, z trzema wybranymi kolumnami. Wynik opisuje bieżący stan tabeli orders; bez filtra i historii nie odpowiada jeszcze na pytanie o konkretny proces.WHERE — których rekordów szukamy
Filtr powinien odpowiadać pytaniu. Warunek po identyfikatorze zawęża jeden obiekt, a warunki po statusie i czasie wybierają zbiór kandydatów do dalszej analizy.
SELECT order_id, order_status, payment_status
FROM orders
WHERE order_status = 'CANCELLED'
AND payment_status = 'PAID';
To zapytanie nie udowadnia błędu. Wybiera zamówienia, dla których trzeba sprawdzić regułę biznesową: czy płatność miała zostać zwrócona, czy anulowanie było dozwolone i czy istnieje odpowiednie zdarzenie integracyjne.
1002. Interpretacja: rekord spełnia oba techniczne warunki. Ograniczenie: bez historii płatności i reguły zwrotu nie wiadomo, czy jest to anomalia, czy dozwolony stan przejściowy.IN, BETWEEN i LIKE
SELECT order_id, order_status
FROM orders
WHERE order_status IN ('NEW', 'PAID', 'PROCESSING');
SELECT order_id, created_at
FROM orders
WHERE created_at >= '2026-07-01'
AND created_at < '2026-08-01';
SELECT order_id, external_order_id
FROM orders
WHERE external_order_id LIKE 'EXT-%';
IN wybiera jeden z podanych statusów, półotwarty zakres dat zwraca cztery lipcowe zamówienia, a LIKE 'EXT-%' dopasowuje identyfikatory o oczekiwanym prefiksie. Zachowanie wielkości liter i znaków narodowych zależy od silnika oraz kolacji. Dla czasu przedział półotwarty eliminuje zgadywanie, czy koniec dnia oznacza sekundy, milisekundy czy inną precyzję.
IN wybiera zamówienia w jednym z trzech aktywnych statusów, zakres dat zwraca cztery rekordy fixture, a wzorzec EXT-% pomija zamówienie bez identyfikatora. Każdy wariant odpowiada na inne pytanie; nie należy łączyć ich wyników w jeden wniosek bez określenia strefy czasowej, kolacji i znaczenia statusów.NULL — brak wartości nie jest zerem
NULL oznacza brak wartości, ale znaczenie biznesowe trzeba ustalić osobno. Może oznaczać brak identyfikatora przed wysłaniem, nieustalony stan albo dane nieobowiązkowe.
SELECT order_id, external_order_id
FROM orders
WHERE external_order_id IS NULL;
1003. To dowód braku wartości w tej kolumnie, nie automatyczny dowód błędu integracji.Porównanie external_order_id = NULL nie jest poprawnym sposobem wyszukiwania braków. SQL używa tu trzywartościowej logiki, dlatego potrzebne są IS NULL i IS NOT NULL.
ORDER BY i ograniczenie wyniku
SELECT order_id, order_status, updated_at
FROM orders
ORDER BY updated_at DESC
FETCH FIRST 20 ROWS ONLY;
updated_at. Przy identycznym czasie warto dodać drugi warunek sortowania, np. order_id DESC.Jeżeli używany silnik wymaga LIMIT 20 albo TOP 20, trzeba zaznaczyć różnicę dialektu. Najważniejsze jest, aby sortowanie było jawne: bez niego „ostatnie rekordy” są tylko przypuszczeniem.
Wynik zapytania a wniosek
Wynik SQL + wymaganie + reguła biznesowa + kontekst procesu = wniosek analityczny.
Jeśli znajdziemy zamówienie z payment_status = 'PAID' i order_status = 'CANCELLED', możliwe są różne wyjaśnienia: oczekiwany zwrot, stan przejściowy, brak synchronizacji albo błąd. Pojedynczy wynik jest dowodem zapisu, nie diagnozą przyczyny.
Najczęstsze błędy
- używanie
SELECT *w każdym zapytaniu; - brak warunku ograniczającego na dużej tabeli;
- porównywanie wartości z
NULLprzez znak równości; - mieszanie
ANDiORbez nawiasów; - uznawanie nazwy kolumny za pełną definicję biznesową;
- traktowanie pojedynczego wyniku jako dowodu przyczyny.
Ćwiczenia analityczne
- Znajdź zamówienia klienta 17.
- Znajdź anulowane zamówienia z płatnością
PAID. - Znajdź zamówienia bez identyfikatora zewnętrznego.
- Znajdź dwadzieścia ostatnio zmienionych rekordów.
- Znajdź aktywne zamówienia z lipca 2026.
Pokaż rozwiązanie 1
SELECT order_id, order_status
FROM orders
WHERE customer_id = 17;Pokaż rozwiązanie 2
SELECT order_id, order_status, payment_status
FROM orders
WHERE order_status = 'CANCELLED'
AND payment_status = 'PAID';Pokaż rozwiązanie 3
SELECT order_id, external_order_id
FROM orders
WHERE external_order_id IS NULL;Pokaż rozwiązanie 4
SELECT order_id, order_status, updated_at
FROM orders
ORDER BY updated_at DESC
FETCH FIRST 20 ROWS ONLY;Pokaż rozwiązanie 5
SELECT order_id, order_status, created_at
FROM orders
WHERE order_status IN ('NEW', 'PAID', 'PROCESSING')
AND created_at >= '2026-07-01'
AND created_at < '2026-08-01';Co dalej?
Jedna tabela wystarcza tylko do prostych pytań. Klient, zamówienie, płatność i historia żyją osobno.