Podzapytania skorelowane (correlated subqueries) i nieskorelowane w SQL na przykładzie MySQL

Podzapytania skorelowane to specyficzny rodzaj budowania zapytań w języku SQL, który pozwala nam na uzyskanie pewnych informacji wprost z bazy danych, które to informacje nie są możliwe do uzyskania wprost za pomocą zwykłych (nieskorelowanych podzapytań). Nie znając konstrukcji skorelowanych podzapytań SQL, musielibyśmy w celu pozyskania niektórych interesujących nas informacji, wykonywać wiele zapytań i podzapytań prostych i dodatkowo obrabiać jeszcze wyniki za pomocą języka programowania. To dokładnie taka sama sytuacja, jak w takim przypadku, gdy chcielibyśmy pobrać informacje z kilku tabel nie znając konstrukcji JOIN.

Na początek rozważmy zwykłe zapytanie (nie mylić z podzapytaniem).

SELECT imie, nazwisko, wiek, plec
FROM osoby

Powyższe zapytanie pobiera z tabeli o nazwie osoby kolumny o nazwie imie, nazwisko wiek oraz plec. Jest to najzwyklejsze zapytanie w języku SQL, które iteruje po wszystkich wierszach tabeli osoby i zwraca nam kolejne wiersze. Teraz, rozważmy zapytanie zawierające zwykłe (nieskorelowane) podzapytanie.

SELECT imie, nazwisko, wiek, plec
FROM osoby
WHERE wiek =
    (SELECT avg(wiek)
    FROM osoby)

Czym to zapytanie różni się od poprzedniego. Po pierwsze pojawił się warunek dotyczący wieku osób, o które nam chodzi. Jednakże wiek nie jest podany wprost, a jest obliczony za pomocą podzapytania. Owe podzapytanie liczy średnią wieku wszystkich osób w tabeli. Reasumując, całe zapytanie zwraca nam te osoby, których wiek jest dokładnie równy średniemu wiekowi wszystkich osób w tabeli. Czemu takie podzapytanie nazywa się nieskorelowanym? Ano dlatego, że wynik zwracany przez podzapytanie nie jest w żaden sposób powiązane z resztą zapytania (zapytaniem zewnętrznym) i dlatego każda osoba z tabeli jest porównywana z tym samym średnim wiekiem wszystkich osób. Teraz rozważmy ostateczny przykład – zapytanie z podzapytaniem skorelowanym.

SELECT imie, nazwisko, wiek, plec
FROM osoby AS o
WHERE wiek =
    (SELECT avg(wiek)
    FROM osoby
    WHERE plec = o.plec);

Czym to zapytanie różni się od poprzedniego. Na pewno w zapytaniu zewnętrznym pojawił się alias dla nazwy tabeli. Po drugie zmieniło się trochę podzapytanie. Nie jest brana średnia wszystkich osób w tabeli. Liczymy średnią dla pewnej, konkretnej płci. Skąd wiemy jaka to płeć? Płeć jest brana z zapytania zewnętrznego właśnie dzięki aliasowi kolumny. Reasumując, teraz podczas filtrowania nie są wybierane tak po prostu osoby, których wiek jest średnią wieku wszystkich osób. Teraz wybierane są te osoby, których wiek jest równy średniej wieku dla danej płci. Czyli baza danych idzie po kolei po wszystkich osobach, dla każdej osoby bierze jej płeć, liczy średnią wieku dla tej płci i na końcu sprawdza, czy wiek danej osoby jest równy średniemu wiekowi dla danej płci. Alias w zapytaniu zewnętrznym był potrzebny po to, żeby nie było nieporozumień w końcowym warunku, gdzie operujemy na tej samej nazwie pola, z tym, że jedno pole odnosi się do zapytania zewnętrznego, a drugie pole odnosi się do podzapytania. Dlaczego nazywa się to podzapytanie skorelowane? Dlatego, że podzapytanie jest zależne od aktualnego stanu zapytania zewnętrznego – jest z nim skorelowane.