Dlaczego to zapytanie działa?

37

Mam dwie tabele, table_a (id, name) i table_b (id), powiedzmy na Oracle 12c.

Dlaczego to zapytanie nie zwraca wyjątku?

select * from table_a where name in (select name from table_b);

Z tego, co rozumiem, Oracle postrzega to jako

select * from table_a where name = name;

Ale nie rozumiem, dlaczego?

eagerMoose
źródło

Odpowiedzi:

61

Zapytanie jest poprawnym składniowo SQL, nawet jeśli table_bnie ma namekolumny. Powodem jest rozdzielczość zakresu.

Podczas analizy zapytania najpierw sprawdza się, czy table_bma namekolumnę. Ponieważ tak nie jest, to table_ajest sprawdzane. Zgłasza błąd tylko wtedy, gdy żadna z tabel nie ma namekolumny.

Na koniec zapytanie jest wykonywane jako:

select a.* 
from table_a  a
where a.name in (select a.name 
                 from table_b  b
                );

Jeśli chodzi o wyniki, zapytanie dałoby, dla każdego wiersza table_apodzapytania (select name from table_b)- lub (select a.name from table_b b)- jest tabelą z pojedynczą kolumną o tej samej a.namewartości i tyle samo wierszy co table_b. Tak więc, jeśli table_bma 1 lub więcej wierszy, zapytanie działa jako:

select a.* 
from table_a  a
where a.name in (a.name, a.name, ..., a.name) ;

lub:

select a.* 
from table_a  a
where a.name = a.name ;

lub:

select a.* 
from table_a  a
where a.name is not null ;

Jeśli table_bjest puste, zapytanie nie zwróci żadnych wierszy (dziękuję @ughai za wskazanie tej możliwości).


To (fakt, że nie pojawia się błąd) jest prawdopodobnie najlepszym powodem, dla którego wszystkie odwołania do kolumn powinny być poprzedzone nazwą / aliasem tabeli. Jeśli zapytanie było:

select a.* from table_a where a.name in (select b.name from table_b); 

od razu dostaniesz błąd. Gdy pominięto prefiksy tabel, nie jest trudno popełnić takie błędy, szczególnie w bardziej złożonych zapytaniach, a co ważniejsze, pozostają niezauważone.

Przeczytaj także w dokumentach Oracle: Rozpoznawanie nazw w statycznych instrukcjach SQL podobny przykład B-6 w przechwytywaniu wewnętrznym i zalecenia w unikaniu wewnętrznego przechwytywania w akapitach instrukcji SELECT i DML :

Kwalifikuj każde odwołanie do kolumny w instrukcji za pomocą odpowiedniego aliasu tabeli.

ypercubeᵀᴹ
źródło
Jak dokładnie przeanalizowałeś wewnętrzne działanie silnika SQL?
RinkyPinku
8

Dlatego

Oracle wykonuje skorelowane podkwerenda, gdy zagnieżdżone podkwerenda odwołuje się do kolumny z tabeli odnoszącej się do instrukcji nadrzędnej jeden poziom powyżej podkwerendy. http://docs.oracle.com/cd/E11882_01/server.112/e41084/queries007.htm#SQLRF52357

Oznacza to, że w celu ustalenia, czy podzapytanie jest skorelowane, Oracle musi spróbować rozwiązać nazwy w podzapytaniu, w tym również w kontekście instrukcji zewnętrznych. A dla nieprefiksowanych namejest to jedyna możliwa rozdzielczość.

Serg
źródło
4

Nie ma w tym namepola, table_bwięc Oracle bierze je z table_a. Próbowałem, EXPLAIN PLANale dało mi to tylko to, że istnieje TABLE ACCESS FULL. Zakładam, że wygeneruje to pewien rodzaj produktu kartezjańskiego między dwiema tabelami, w wyniku czego lista wszystkich nazw table_azostanie zwrócona przez zapytanie.

Marco
źródło
5
„W table_b nie ma pola nazwy, więc Oracle bierze to z table_a.” Poprawny. „Zakładam, że wygeneruje to pewien rodzaj produktu kartezjańskiego”. Źle. Zapytanie ma from table_a where .... Zwróci wszystkie wiersze z table_awyjątkiem tych, które namesą puste.
ypercubeᵀᴹ
1
TABLE ACCESS FULLto tylko sposób, w jaki Oracle informuje cię, że wykonuje skanowanie sekwencyjne.
Joishi Bodio
1
Twój PLAN nie ma znaczenia - może być indeksowanie z dużymi tabelami - Zakładam, że działasz na danych testowych?
Vérace