Pourquoi cette requête fonctionne-t-elle?

37

J'ai deux tables, table_a (id, nom) et table_b (id), disons sur Oracle 12c.

Pourquoi cette requête ne renvoie-t-elle pas une exception?

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

D'après ce que j'ai compris, Oracle voit cela comme

select * from table_a where name = name;

Mais ce que je ne comprends pas, c'est pourquoi?

empressé
la source

Réponses:

61

La requête est syntaxiquement correcte, même si SQL table_bn'a pas de namecolonne. La raison est la résolution de la portée.

Lorsque la requête est analysée, il est d'abord vérifié s'il table_bcontient une namecolonne. Puisqu'il ne le fait pas, alors table_aest vérifié. Une erreur ne serait générée que si aucune des tables n'avait de namecolonne.

Enfin, la requête est exécutée comme suit:

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

En ce qui concerne les résultats, la requête donnerait, pour chaque ligne de table_ala sous (select name from table_b)- requête - ou (select a.name from table_b b)- est une table avec une seule colonne avec la même a.namevaleur et autant de lignes que table_b. Donc, si table_ba 1 ou plusieurs lignes, la requête s'exécute en tant que:

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

ou:

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

ou:

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

Si table_best vide, la requête ne renverra aucune ligne (thnx à @ughai pour indiquer cette possibilité).


Cela (le fait que vous n'ayez pas d'erreur) est probablement la meilleure raison pour que toutes les références de colonne soient préfixées du nom / alias de la table. Si la requête était:

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

vous auriez eu l'erreur tout de suite. Lorsque les préfixes de table sont omis, il n'est pas difficile que de telles erreurs se produisent, en particulier dans les requêtes plus complexes, et plus important encore, passent inaperçues.

Lisez également dans Oracle docs: Résolution de noms dans des instructions SQL statiques, exemple B-6 similaire en capture interne et recommandations dans les paragraphes Eviter la capture interne dans les instructions SELECT et DML :

Qualifiez chaque référence de colonne dans l'instruction avec l'alias de table approprié.

ypercubeᵀᴹ
la source
Comment avez-vous disséqué le fonctionnement interne du moteur SQL avec une telle précision?
RinkyPinku
8

Car

Oracle exécute une sous-requête corrélée lorsqu'une sous-requête imbriquée fait référence à une colonne d'une table faisant référence à une instruction parent un niveau supérieur à la sous-requête. http://docs.oracle.com/cd/E11882_01/server.112/e41084/queries007.htm#SQLRF52357

Cela signifie que pour déterminer si la sous-requête est corrélée, Oracle doit également tenter de résoudre les noms dans la sous-requête, y compris le contexte de l'instruction externe. Et pour unprefixed namec'est la seule résolution possible.

Serg
la source
4

Il n'y a pas de namechamp dans la base de données table_bOracle table_a. J'ai essayé le EXPLAIN PLANmais cela m'a donné seulement qu'il y a un TABLE ACCESS FULL. Je suppose que cela générera une sorte de produit cartésien entre les deux tables, ce qui donnera une liste de tous les noms qui table_asont renvoyés par la sous-requête.

Marco
la source
5
"Il n'y a pas de champ de nom dans table_b, donc Oracle prend celui de table_a." Correct. "Je suppose que cela va générer une sorte de produit cartésien." Faux. La requête a from table_a where .... Il renverra toutes les lignes table_asauf celles qui namesont nulles.
Ypercube at
1
TABLE ACCESS FULLC’est juste la façon dont Oracle vous dit qu’elle effectue une analyse séquentielle.
Joishi Bodio
1
Votre PLAN n’est pas pertinent - il se peut qu’il y ait une indexation avec d’énormes tables - je suppose que vous utilisez des données de test?
Vérace