Dziś zanurzymy się w jeszcze bardziej specyficzny, ale ważny temat: funkcje GREATEST() i LEAST(). Dowiesz się, jak znaleźć największe i najmniejsze wartości z kilku kolumn i – co najważniejsze – jak NULL wpływa na ich działanie.
Jeśli kiedyś szukałeś najważniejszej rzeczy w swoim życiu (miłości, wymarzonej pracy albo najlepszego przepisu na pizzę), to od razu załapiesz, po co są funkcje GREATEST() i LEAST(). Te funkcje pomagają znaleźć największą lub najmniejszą wartość na liście rzeczy. Tylko zamiast pizzy pracujesz z liczbami, datami, stringami i innymi danymi w PostgreSQL.
GREATEST()
GREATEST() zwraca największą wartość z przekazanego zestawu.
Składnia:
GREATEST(value1, value2, ..., valueN)
LEAST()
LEAST() robi odwrotnie: szuka najmniejszej wartości.
Składnia:
LEAST(value1, value2, ..., valueN)
Przykład:
Załóżmy, że mamy tabelę students_scores, gdzie trzymamy oceny studentów z trzech egzaminów:
| student_id | exam_1 | exam_2 | exam_3 |
|---|---|---|---|
| 1 | 85 | 90 | 82 |
| 2 | NULL | 76 | 89 |
| 3 | 94 | NULL | 88 |
Użycie GREATEST() i LEAST():
SELECT
student_id,
GREATEST(exam_1, exam_2, exam_3) AS highest_score,
LEAST(exam_1, exam_2, exam_3) AS lowest_score
FROM students_scores;
Wynik:
| student_id | highest_score | lowest_score |
|---|---|---|
| 1 | 90 | 82 |
| 2 | 89 | NULL |
| 3 | 94 | NULL |
Jak NULL wpływa na GREATEST() i LEAST()
No i teraz robi się najciekawiej. W tabeli razem z wartościami mogą pojawić się też NULL. A jak już wiemy, NULL to taka tajemnicza istota, która oznacza brak danych albo nieznaną wartość. Sprawdźmy, co się dzieje, jeśli NULL trafi do funkcji GREATEST() i LEAST() w PostgreSQL.
Zachowanie NULL:
W PostgreSQL funkcje GREATEST() i LEAST() mają specjalne zachowanie: ignorują wartości NULL przy szukaniu największej lub najmniejszej wartości wśród swoich argumentów. Ważne: Jedyny przypadek, kiedy te funkcje zwrócą NULL, to jeśli wszystkie ich argumenty są NULL.
Przykład:
SELECT
GREATEST(10, 20, NULL, 5) AS greatest_value,
LEAST(10, 20, NULL, 5) AS least_value;
Wynik:
| greatest_value | least_value |
|---|---|
| 20 | 5 |
Jak widzisz, NULL został zignorowany i funkcje zwróciły największą i najmniejszą wartość z obecnych (10, 20, 5).
A tu przykład, gdy wszystkie argumenty to NULL:
Przykład:
SELECT
GREATEST(NULL, NULL) AS greatest_nulls,
LEAST(NULL, NULL) AS least_nulls;
Wynik:
| greatest_nulls | least_nulls |
|---|---|
| NULL | NULL |
Jak uniknąć problemów z NULL?
Chociaż PostgreSQL domyślnie ignoruje NULL, czasem możesz chcieć inne zachowanie. Na przykład, jeśli chcesz, żeby NULL był traktowany jako konkretna wartość (np. 0 albo coś innego domyślnego) przy określaniu największej/najmniejszej. W takich sytuacjach możesz użyć funkcji COALESCE().
Funkcja COALESCE(arg1, arg2, ...) zwraca pierwszy nie-NULL argument ze swojej listy. Dzięki temu możesz wcześniej podmienić NULL na sensowną wartość przed przekazaniem do GREATEST() albo LEAST().
Przykład 1: Zamiana NULL na 0
Załóżmy, że chcemy uznać brak oceny NULL za 0. Możemy użyć COALESCE(), żeby podstawić wartość domyślną.
Oto nasza wyjściowa tabela:
| student_id | exam_1 | exam_2 | exam_3 |
|---|---|---|---|
| 1 | 90 | 85 | 82 |
| 2 | NULL | 89 | NULL |
| 3 | NULL | NULL | 94 |
Zapytanie:
SELECT
student_id,
GREATEST(
COALESCE(exam_1, 0),
COALESCE(exam_2, 0),
COALESCE(exam_3, 0)
) AS highest_score,
LEAST(
COALESCE(exam_1, 0),
COALESCE(exam_2, 0),
COALESCE(exam_3, 0)
) AS lowest_score
FROM students_scores;
Wynik:
| student_id | highest_score | lowest_score |
|---|---|---|
| 1 | 90 | 82 |
| 2 | 89 | 0 |
| 3 | 94 | 0 |
Przykład 2: Zamiana NULL na wartość z innej kolumny
Czasem zamiast stałej wartości (np. 0) trzeba podstawić wartość z innej kolumny. Na przykład, jeśli exam_3 nie ma, chcemy użyć wartości z exam_1.
SELECT
student_id,
GREATEST(
exam_1,
exam_2,
COALESCE(exam_3, exam_1)
) AS highest_score
FROM students_scores;
Załóżmy, że mamy taką tabelę:
| student_id | exam_1 | exam_2 | exam_3 |
|---|---|---|---|
| 1 | 90 | 85 | 82 |
| 2 | NULL | 89 | NULL |
| 3 | 70 | NULL | NULL |
Wynik działania zapytania:
| student_id | highest_score |
|---|---|
| 1 | 90 |
| 2 | 89 |
| 3 | 70 |
Praktyczne case'y
Case 1: Szukanie maksymalnej zniżki
| order_id | discount_1 | discount_2 | discount_3 |
|---|---|---|---|
| 101 | 5 | 10 | 7 |
| 102 | NULL | 3 | 8 |
| 103 | 15 | NULL | NULL |
| 104 | NULL | NULL | NULL |
Pracujesz z tabelą orders, gdzie każde zamówienie może mieć trzy różne typy zniżek. Musisz znaleźć największą ze wszystkich zniżek dla każdego zamówienia.
SELECT
order_id,
GREATEST(discount_1, discount_2, discount_3) AS max_discount
FROM orders;
Wynik:
| order_id | max_discount |
|---|---|
| 101 | 10 |
| 102 | 8 |
| 103 | 15 |
| 104 | NULL |
Case 2: Szukanie minimalnej ceny produktu
W tabeli products trzymane są ceny produktów w trzech walutach (USD, EUR, GBP). Twoje zadanie – znaleźć minimalną cenę dla każdego produktu.
| product_id | price_usd | price_eur | price_gbp |
|---|---|---|---|
| 1 | 100 | 95 | 80 |
| 2 | NULL | 150 | 140 |
| 3 | 200 | NULL | NULL |
| 4 | NULL | NULL | NULL |
SELECT
product_id,
LEAST(price_usd, price_eur, price_gbp) AS lowest_price
FROM products;
| product_id | lowest_price |
|---|---|
| 1 | 80 |
| 2 | 140 |
| 3 | 200 |
| 4 | NULL |
Jeśli wszystkie ceny to NULL, wynik też będzie NULL
Typowe błędy przy użyciu GREATEST() i LEAST()
Błąd 1: Nieoczekiwany wynik przez NULL.
Wcześniej w wykładzie dokładnie omówiliśmy, jak NULL wpływa na GREATEST() i LEAST() w PostgreSQL. Główny błąd polega na tym, że użytkownicy przyzwyczajeni do zachowania NULL w innych bazach (gdzie jeden NULL "zatruwa" cały wynik), spodziewają się tego samego po PostgreSQL.
Jak się objawia błąd: Możesz błędnie założyć, że jeśli wśród argumentów jest NULL, funkcja zawsze zwróci NULL. W efekcie możesz niepotrzebnie używać COALESCE() do wszystkich argumentów, co może skomplikować zapytanie i spowolnić jego wykonanie, jeśli w twoim przypadku NULL powinien być po prostu zignorowany.
Błąd 2: Użycie GREATEST() i LEAST() z niekompatybilnymi typami.
Funkcje GREATEST() i LEAST() są przeznaczone do porównywania wartości tego samego typu danych albo typów, które mogą być niejawnie przekonwertowane do siebie. Próba porównania zupełnie różnych, niekompatybilnych typów skończy się błędem.
Jak się objawia błąd: Dostaniesz komunikat o błędzie, wskazujący na niezgodność typów danych.
GO TO FULL VERSION