Wyrażenie IN w SQL'u pozwala uprościć przekazywanie do zapytania listy warunków.
Wyobraź sobie, że tabele z markami samochodów. Do głowy powinny Ci przyjść nazwy najbardziej popularnych producentów: Volvo, Tesla, Audi, Volkswagen, Ford, Toyota, Honda itd. Chciałbyś ograniczyć listę wyników tylko do trzech najbardziej popularnych w Polsce marek: Toyota, Volkswagen i Audi.
Najprościej zrobić to w ten sposób:
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN ('Toyota', 'Volkswagen', 'Audi')
Co jest równoznaczne z:
SELECT * FROM dbo.Manufacturer
WHERE Automaker = 'Toyota'
OR Automaker = 'Volkswagen'
OR Automaker = 'Audi'
Jeżeli chcesz odfiltrować samochody najbardziej popularne i ich NIE pokazywać:
SELECT * FROM dbo.Manufacturer
WHERE Automaker NOT IN ('Toyota', 'Volkswagen', 'Audi')
Dodajesz słówko kluczowe NOT. W wynikach dostaniesz wszystkich producentów samochodów poza Toyotą, Volkswagenem i Audi.
Czytaj dalej, dowiesz się o bardziej zaawansowanych zastosowaniach i jak uczynić swój kod odpornym na błędy.
SQL IN - lista wartości
W powyższym przykładzie, listę wartości wprowadzałeś ręcznie. Natomiast wartości mogą pochodzić też z wyniku zapytania:
Przykład:
SELECT * FROM dbo.VehicleAdv
WHERE Automaker IN (SELECT Automaker FROM dbo.TopAdvJune )
Nasza wyobraźnia mogłaby pójść o krok dalej i na przykład będziesz chciał ograniczyć wartości używając dwóch kolumn i warunku IN:
SELECT * FROM dbo.VehicleAdv
WHERE (Automaker, Model) IN (SELECT Automaker, Model FROM dbo.TopAdvJune )
Niestety SQL Server NIE pozwala na takie operacje. Jeżeli pracujesz z innymi bazami danych sprawdź, bądź ciekawy! Na przykład Netezza pozwala na takie zapytania.
SQL IN - dla dociekliwych
Wyobraź sobie, że chcesz podać po przecinku listę 1000 wartości? Czy wtedy to się uda?
W zależności od bazy danych, możesz się zetknąć z różnymi ograniczeniami. Tutaj najlepiej odwołać się do dokumentacji bazy z którą pracujesz. Dla SQL Servera https://docs.microsoft.com/en-us/sql/t-sql/language-elements/in-transact-sql?view=sql-server-ver15 w paragrafie Remarks masz szczegóły.
Albo co zwróci zapytanie:
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN (NULL)
Tutaj nic nie zostanie zwrócone.
Uwaga zwróć proszę uwagę, że tutaj też nic nie zostanie zwrócone.
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN (SELECT Automaker FROM dbo.Invoice WHERE 1 = 2)
Albo
SELECT * FROM dbo.Manufacturer
WHERE Automaker NOT IN (NULL)
Co ciekawe, tutaj też nic nie zostanie zwrócone.
A co jeżeli wartość, której szukasz, wpiszesz dwa razy na liście? Czy dostaniesz duplikaty?
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN ('Volvo', 'Volvo')
Rezultat będzie taki sam, jeżeli wpiszesz Volvo jeden raz lub wiele razy.
A co jeżeli wpiszesz wartość, której na pewno nie ma na liście? Co wtedy zostanie zwrócone?
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN ('Polonez')
Po pierwsze Polonez to nie producent tylko model i nie ma go w tabeli dbo.Manufacturer. Czyli takie zapytanie nie zwróci żadnych wyników.
Porównanie ciągu znaków (Automaker) z kolumną typu liczbowego (AndTk), zwróci błąd.
SELECT * FROM dbo.Manufacturer
WHERE Automaker IN (SELECT AdvTk FROM dbo.VehicleAdv)
Czy to są równoznaczne zapytania:
SELECT * FROM dbo.VehicleAdv
WHERE Automaker IN ('Toyota', 'Volkswagen', 'Audi')
AND Color = 'Red'
SELECT * FROM dbo.VehicleAdv
WHERE Automaker = 'Toyota'
OR Automaker = 'Volkswagen'
OR Automaker = 'Audi'
AND Color = 'Red'
Oczywiście, że NIE są to takie same zapytania. W pierwszym przypadku dostaniesz wszystkie czerwone Toyoty, VW i Audi. W drugim zapytaniu tylko Audi będzie czerwone.
SQL IN - Wielkość ma znaczenie (czasami)
Wyobraź sobie, że w kolumnie z markami samochodów jest wpisane:
- Volvo
- VOLVO
- volvo
Niby to samo, jednak silnik bazy danych może interpretować te wartości inaczej.
Wielkość będzie miała znaczenia gdy:
- Kolumna będzie zdefiniowana w sposób wrażliwy na wielkość liter.
- Baza danych będzie wrażliwa na wielkość liter
Dla silnika bazy danych wielkość liter przy porównaniu może mieć znaczenie. Na przykład w SQL Server możesz się spotkać z ustawieniami collation, i zobaczysz, że wielkość znaków będzie ważna przy porównaniu. Wtedy duże 'VOLVO' i małe 'volvo', będą dla bazy danych stanowiły inne wartości.
Pytanie jak Ty chciałbyś je traktować? Czy chciałbyś je traktować jako te same wartości? Czy jako różne wartości?
Z zależności od potrzeby, zastosowanie funkcji lower albo upper przy porównaniu oszczędzi wielu zagadek w przyszłości.
To dobra praktyka, którą albo możesz pożyczyć albo zignorować.
Oczekuj, że dane mogą się zmienić, nie będą czyste i przygotuj się na to.
SELECT * FROM dbo.Manufacturer
WHERE lower(Automaker) IN ('toyota', 'volkswagen', 'audi')
Pozwoli Ci to uniknąć wielu problemów w przyszłości, gdy założysz, że dane które otrzymasz nie będą czyste.
SQL IN - Aktywne uczenie
Pytania:
- Czy w wyrażeniu IN możesz przekazać tylko jedną wartość? Np.:
SELECT * FROM dbo.Manufacturer WHERE Automaker IN ('Toyota')
- Czy w wyrażeniu IN możesz przekazać wynik zwracany przez inne zapytanie?
- Czy możesz założyć, że zawsze dostaniesz wartości napisane w ten sam sposób?
Odpowiedzi:
- Tak to jest poprawne zapytanie.
- Tak:
SELECT * FROM dbo.Manufacturer WHERE Automaker IN (SELECT Automaker FROM dbo.VehicleAdv)
- 🙂 Mam nadzieję, że udało mi się Ciebie przekonać, że możesz się spodziewać różnych wartości.
Szukasz więcej informacji na temat SQL'a zajrzyj na:
https://nextlevelbi.pl/next-level-sql/
W formie wideo opowiadałem o IN w SQL w tym wideo: