Почему этот запрос работает?


37

У меня есть две таблицы, table_a (идентификатор, имя) и table_b (идентификатор), скажем, на Oracle 12c.

Почему этот запрос не возвращает исключение?

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

Из того, что я понимаю, Oracle видит это как

select * from table_a where name = name;

Но что я не понимаю, почему?

Ответы:


61

Запрос является синтаксически правильным SQL, даже если table_bне имеет nameстолбца. Причина в разрешении области.

Когда запрос анализируется, сначала проверяется table_bналичие nameстолбца. Так как это не так, то table_aпроверяется. Это выдаст ошибку, только если ни в одной из таблиц не будет nameстолбца.

Наконец запрос выполняется как:

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

Что касается результатов, то запрос даст для каждой строки table_aподзапроса (select name from table_b)- или (select a.name from table_b b)- таблицу с одним столбцом с тем же a.nameзначением и таким же количеством строк, как table_b. Итак, если table_bесть 1 или более строк, запрос выполняется так:

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

или:

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

или:

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

Если table_bпусто, запрос не будет возвращать строк (thnx в @ughai за указание этой возможности).


Это (тот факт, что вы не получаете сообщение об ошибке), вероятно, является лучшей причиной того, что все ссылки на столбцы должны начинаться с имени таблицы / псевдонима. Если запрос был:

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

Вы бы сразу получили ошибку. Когда префиксы таблиц опущены, такие ошибки не возникают, особенно в более сложных запросах, и, что еще важнее, остаются незамеченными.

Читайте также в документации Oracle: Разрешение имен в статических операторах SQL, аналогичный пример B-6 во Внутреннем захвате и рекомендации во избежании внутреннего захвата в параграфах SELECT и DML Statements :

Укажите для каждой ссылки на столбец в операторе соответствующий псевдоним таблицы.


Как вы так точно анализировали внутреннюю работу механизма SQL?
RinkyPinku

8

Потому что

Oracle выполняет коррелированный подзапрос, когда вложенный подзапрос ссылается на столбец из таблицы, ссылающейся на родительский оператор на один уровень выше подзапроса. http://docs.oracle.com/cd/E11882_01/server.112/e41084/queries007.htm#SQLRF52357

Это означает, что для определения того, является ли подзапрос коррелированным, Oracle должен попытаться разрешить имена в подзапросе, включая внешний контекст оператора. А для без префиксов nameэто единственное возможное разрешение.


4

nameПоле отсутствует, table_bпоэтому Oracle его берет table_a. Я попробовал, EXPLAIN PLANно это дало мне только то, что есть TABLE ACCESS FULL. Я предполагаю, что это сгенерирует некоторый вид декартового произведения между обеими таблицами, в результате чего table_aподзапрос возвращает список всех имен .


5
«В table_b нет поля имени, поэтому Oracle берет его из table_a». Правильный. «Я предполагаю, что это создаст какой-то декартовый продукт». Неправильно. Запрос имеет from table_a where .... Он вернет все строки, table_aкроме тех, которые nameявляются нулевыми.
ypercubeᵀᴹ

1
TABLE ACCESS FULLэто просто способ Oracle сказать вам, что он выполняет последовательное сканирование.
Джоиши Бодио

1
Ваш ПЛАН не имеет значения - вполне может быть индексация с огромными таблицами - я предполагаю, что вы работаете с тестовыми данными?
Верас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.