Semi JOIN realizowany przez EXISTS zwraca wiersze tabeli lewej mające dopasowanie w prawej, bez powielania wyników.
Istota semijoinu wyrażonego przez EXISTS
Semi JOIN to operacja relacyjna, która wybiera wiersze z jednej, lewej relacji na podstawie istnienia co najmniej jednego powiązanego wiersza w relacji prawej. W zapytaniu SQL najczęściej zapisuje się ją za pomocą predykatu EXISTS. Wynik zawiera kolumny pochodzące z zapytania zewnętrznego, natomiast sama podkwerenda służy wyłącznie do sprawdzenia, czy dopasowanie istnieje. Nie przekazuje ona do wyniku swoich kolumn i nie powoduje zwielokrotnienia wiersza wtedy, gdy po prawej stronie znajduje się kilka pasujących rekordów.
Przykładowo można wyszukać klientów, którzy mają przynajmniej jedno zamówienie, bez pobierania danych zamówień:
SELECT c.id, c.name
FROM customers AS c
WHERE EXISTS (
SELECT 1
FROM orders AS o
WHERE o.customer_id = c.id
);
Jak działa korelacja między zapytaniem zewnętrznym a podzapytaniem
W tym przykładzie zapytanie wewnętrzne jest skorelowane z zapytaniem zewnętrznym, ponieważ odwołuje się do c.id, czyli wartości pochodzącej z aktualnie rozpatrywanego wiersza tabeli customers. Dla każdego klienta sprawdzany jest warunek o.customer_id = c.id. Jeżeli baza znajdzie choć jeden rekord w tabeli orders, predykat EXISTS przyjmuje wartość prawdziwą i klient trafia do wyniku. Jeżeli nie znajdzie żadnego dopasowania, wiersz zostaje pominięty.
Wewnątrz EXISTS zwykle stosuje się SELECT 1, ponieważ wartość zwracanej kolumny nie ma znaczenia dla wyniku logicznego. Równie poprawne może być użycie innej stałej albo nazwy kolumny, ale odczytywanie danych z podzapytania nie jest potrzebne do samego testu istnienia. Istotne są warunki w klauzuli WHERE, które określają, co należy uznać za dopasowanie. Korelacja musi odnosić się do właściwych kluczy lub innych pól opisujących relację między tabelami; błędny warunek może sprawić, że każdy wiersz zostanie uznany za pasujący albo że wynik będzie pusty.
Brak duplikatów i różnica względem zwykłego JOIN
Podstawową cechą Semi JOIN jest zachowanie najwyżej jednego wyniku dla każdego wiersza lewej tabeli. Jeśli jeden klient ma dziesięć zamówień spełniających warunek, zapytanie z EXISTS nadal zwróci tego klienta tylko raz. Dzieje się tak dlatego, że podzapytanie nie jest dołączane do zbioru wynikowego jako druga tabela. Jest używane jako test logiczny: wynik jest prawdziwy albo fałszywy.
Dla porównania, zapytanie zapisane z INNER JOIN może zwrócić klienta wielokrotnie, gdy istnieje wiele pasujących zamówień:
SELECT c.id, c.name
FROM customers AS c
INNER JOIN orders AS o
ON o.customer_id = c.id;
W takim przypadku każdy pasujący wiersz z orders tworzy połączenie z wierszem klienta. Duplikaty można później ograniczać przez DISTINCT, ale zmienia to sposób budowy zapytania i może wymagać dodatkowego przetwarzania. EXISTS wyraża zamiar bardziej bezpośrednio: nie chodzi o pobranie zamówień, tylko o sprawdzenie, czy klient posiada przynajmniej jedno zamówienie. Z kolei NOT EXISTS realizuje warunek odwrotny i pozwala znaleźć wiersze, dla których nie istnieje żadne dopasowanie, na przykład klientów bez zamówień.
Warunki poprawnego użycia w zapytaniach SQL
Podzapytanie używane z EXISTS powinno zawierać warunek wiążący je z wierszem zapytania zewnętrznego. Bez takiej korelacji test może być wykonywany jako jeden wspólny warunek dla całego wyniku. Przykład WHERE EXISTS (SELECT 1 FROM orders) sprawdza jedynie, czy tabela orders zawiera jakikolwiek rekord. Jeżeli tabela nie jest pusta, warunek będzie prawdziwy dla każdego klienta, niezależnie od tego, czy konkretny klient ma zamówienie.
Znaczenie ma także logika wartości NULL. Jeżeli pole używane do powiązania nie zawiera wartości, porównanie równości z takim polem nie daje wartości prawdziwej, więc dany rekord może nie zostać uznany za dopasowany. Przy projektowaniu warunku trzeba rozróżnić sprawdzanie istnienia powiązanego rekordu od sprawdzania konkretnej wartości w jego kolumnie. Optymalizator bazy danych może przekształcić EXISTS w plan odpowiadający semijoinowi i zakończyć poszukiwanie po znalezieniu pierwszego zgodnego wiersza, ale dokładny plan zależy od silnika, statystyk, rozmiaru tabel oraz dostępnych indeksów.
W praktyce warto kontrolować plan wykonania i sprawdzić, czy kolumna używana do korelacji ma odpowiednią strukturę indeksu. Indeks nie zmienia znaczenia logicznego zapytania, lecz może ograniczyć liczbę odczytywanych rekordów. Sam fakt użycia EXISTS nie gwarantuje automatycznie szybkiego działania. O poprawności decydują przede wszystkim właściwe relacje między tabelami, jednoznaczny warunek korelacji oraz zgodność zapytania z tym, czy wynik ma zawierać wiersze lewej tabeli, czy również dane z tabeli prawej.