next level sql in

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')

sql in

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')

sql not in

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:

  1. Kolumna będzie zdefiniowana w sposób wrażliwy na wielkość liter.
  2. 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:

  1. Czy w wyrażeniu IN możesz przekazać tylko jedną wartość? Np.:
    SELECT * FROM dbo.Manufacturer
    WHERE Automaker IN ('Toyota')
  2. Czy w wyrażeniu IN możesz przekazać wynik zwracany przez inne zapytanie?
  3. Czy możesz założyć, że zawsze dostaniesz wartości napisane w ten sam sposób?

Odpowiedzi:

  1. Tak to jest poprawne zapytanie.
  2. Tak:
    SELECT * FROM dbo.Manufacturer
    WHERE Automaker IN (SELECT Automaker FROM dbo.VehicleAdv)
  3. 🙂 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: