Wprowadzenie

Zapytania podrzędne/podzapytania/zagnieżdzone/cząstkowe są specjalnym rodzajem zapytań SQL używanym wewnątrz klauzuli WHERE.

Tego rodzaju zapytań używamy do zwrócenia danych, które zostały użyte w głównym zapytaniu jako warunek do dalszego ograniczenia interesujących nas rekordów, które mają zostać zwrócone.

Podzapytań możemy używać dla poleceń SELECT, INSERT, UPDATE oraz DELETE. Dodatkowo możemy korzystać z różnych operatorów, tj. =, <, >, <=, >=, IN, BETWEEN, itd.

Zanim przejdziemy do pierwszych eksperymentów musimy poznać zasady jakie kierują zapytaniami zagnieżdzonymi:

  • podzapytania muszą być ograniczone nawiasami;
  • podzapytanie może mieć tylko jedną kolumnę w klauzuli SELECT chyba, że w głównym zapytaniu zdefiniowaliśmy wiele kolumn;
  • w podzapytaniach nie możemy używać klauzuli ORDER BY - zasada ta nie dotyczy głównego zapytania;
  • podzapytania zwracające więcej niż jeden rekord mogą być używane tylko z operatorami wartościowymi takimi jak IN;
  • podzapytanie nie może być używane w funkcji SET;
  • operatora BETWEEN nie możemy używać z podzapytaniem. Możemy go jednak używać w samym podzapytaniu.

Podzapytania z instrukcją SELECT

Podzapytania najczęściej są używane z instrukcją SELECT. Podstawowa składnia prezentuje się w poniższy sposób:

SELECT nazwa_kolumny [nazwy_kolumn]
FROM nazwa_tabeli [nazwy_tabel]
WHERE nazwa_kolumny OPERATOR
	(SELECT nazwa_kolumny [nazwy_kolumn]
	FROM nazwa_tabeli [nazwy_tabel]
	[warunki])

Przykładowe zapytanie dla tabeli Production.Product może przyjąć poniższą formę:

SELECT * FROM Production.Product
WHERE ProductID IN (SELECT ProductID
    FROM Production.Product
    WHERE ReorderPoint > 375)

Jednym z najpopularniejszych przypadków użycia tego podzapytania jest zwrócenie wszystkich użytkowników (jedna tabela), którzy wykonali np. daną akcję w naszym systemie (np. rozwiązali test na inteligencje – informacja przechowywana w innej tabeli).

Podzapytania w instrukcji INSERT

Podzapytania mogą być również używane z instrukcją INSERT. Podzapytanie zwraca wyniki, które zostaną wstawione do innej tabeli. Podstawowa składania zapytania:

INSERT INTO nazwa_tabeli [nazwa_kolumny/nazwy_kolumn]
SELECT [nazwa_kolumny/nazwy_kolumn]
    FROM nazwa_tabeli
    [WHERE operator_wartościowy]

W tym wypadku wykorzystamy wiedzę zdobytą w jednym z poprzednich wpisów dotyczących klonowania tabel. Na bazie tabeli Person.Person utworzymy nową, pustą tabelę, do której wstawimy wybrane rekordy wykorzystując podzapytanie. Wpis o klonowaniu znajdziecie tutaj: SQL - klonowanie tabel

Bazując na powyższym wpisie dodałem nową (pustą tabelę) o nazwie Person.Person2. W celu dodania (wybranych) rekordów posłużę się poniższym poleceniem:

INSERT INTO Person.Person2 (BusinessEntityID, PersonType, NameStyle, Title, FirstName, MiddleName, LastName, Suffix, EmailPromotion, AdditionalContactInfo, Demographics,rowguid, ModifiedDate)
SELECT BusinessEntityID, PersonType, NameStyle, Title, FirstName, MiddleName, LastName, Suffix, EmailPromotion, AdditionalContactInfo, Demographics,rowguid, ModifiedDate
    FROM Person.Person
    WHERE BusinessEntityID IN (1,2,3,4,5,6,7,8,9,10)
Powyższe polecenie skutkuje dodaniem do nowo utworzonej tabeli 10 (wskazanych w podzapytaniu) rekordów.

Podzapytania w instrukcji UPDATE

Idąc dalej tropem tabeli Person.Person2 dokonamy aktualizacji rekordów znajdujących się w tabeli Person.Person. Dzięki takiemu podejściu ograniczymy zmiany do jedynie 10 rekordów w tabeli głównej. Spójrzcie jak wygląda składania zapytania:

UPDATE nazwa_tabeli
SET nazwa_kolumny = wartosc
    [WHERE oprator wartościowy
	(SELECT nazwa_kolumny
	FROM nazwa_tabeli
	[WHERE]]

Dla każdej z osób znajdujących się w sklonowanej tabeli podzielimy wartość z kolumny EmailPromotion przez 3 (to tylko przykład ). W tym celu wykorzystamy poniższe polecenie:

-- dlaczego dzielimy a nie mnożymy?
-- na kolumnę EmailPromotion nałożone zostało ograniczenie CHECK
-- maksymalna wartość musi znaleźć się w zakresie 0 - 2
UPDATE Person.Person
SET EmailPromotion = EmailPromotion / 3
WHERE BusinessEntityID IN (
	SELECT BusinessEntityID 
	FROM Person.Person2);

Podzapytania w instrukcji DELETE

Podzapytań możemy również używać w instrukcji DELETE. Składnia jest niezwykle prosta:

DELETE FROM nazwa_tabeli
[WHERE operator_wartosciowy
	(SELECT nazwa_kolumny
	FROM nazwa_tabeli
	[WHERE])

Tym razem dokonamy usunięcia wszystkich rekordów z tabeli Person.Person2, które znajdują się w tabeli bazowej, tj. Person.Person. Polecenie, którego użyjemy przyjmuje poniższą formę:

-- polecenie dokona usunięcia 10 rekordów - na bazie naszych wcześniejszych zapytań
DELETE FROM Person.Person2
WHERE BusinessEntityID IN (
	SELECT BusinessEntityID
	FROM Person.Person)

Podsumowanie

Udało nam się wspólnie przejść przez kolejny cykl. Mam nadzieję, że wszystkie wpisy został napisane w prosty i zrozumiały sposób a każdy z Was nauczył się czegoś nowego.

Wraz z publikacją tego wpisu dokonałem odświeżenia starszych artykułów, które pokazują możliwości języka T-SQL. Jeżeli nie mieliście jeszcze okazji się z nimi zapoznać a interesują Was możliwości języka zachęcam do lektury: