Чому цей запит працює?


37

У мене дві таблиці, table_a (id, ім'я) та table_b (id), скажімо, на 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 :

Кваліфікуйте кожне посилання стовпця у виписці з відповідним псевдонімом таблиці.


Як ви так точно розсікали внутрішню роботу двигуна 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
"У таблиці_b немає поля імен, тому Oracle бере це з table_a." Правильно. "Я припускаю, що це породжує декартовий продукт". Неправильно. Запит є from table_a where .... Він поверне всі рядки, table_aкрім тих, які nameє недійсними.
ypercubeᵀᴹ

1
TABLE ACCESS FULLце лише спосіб Oracle сказати вам, що він робить послідовне сканування.
Joishi Bodio

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