Question

Mon patron a trouvé un bug dans une requête que j'ai créée et je ne comprends pas le raisonnement derrière ce bug, bien que les résultats de la requête prouvent qu'il a raison.Voici la requête (version simplifiée) avant le correctif :

select PTNO,PTNM,CATCD
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD); 

et le voici après le correctif :

select PTNO,PTNM,PARTS.CATCD
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD); 

Le bug était que des valeurs nulles étaient affichées pour la colonne CATCD, c'est-à-direles résultats de la requête incluaient les résultats de la table CATEGORIES au lieu de PARTS.Voici ce que je ne comprends pas :s'il y avait une ambiguïté dans la requête d'origine, pourquoi Oracle n'a-t-il pas généré d'erreur ?D'après ce que j'ai compris, dans le cas de jointures à gauche, la table "principale" de la requête (PARTS) a la priorité en cas d'ambiguïté.Est-ce que je me trompe ou je ne réfléchis tout simplement pas correctement à ce problème ?

Mise à jour:

Voici un exemple révisé, où l'erreur d'ambiguïté n'est pas générée :

CREATE TABLE PARTS (PTNO NUMBER, CATCD NUMBER, SECCD NUMBER);

CREATE TABLE CATEGORIES(CATCD NUMBER);

CREATE TABLE SECTIONS(SECCD NUMBER, CATCD NUMBER);


select PTNO,CATCD 
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD) 
left join SECTIONS on (SECTIONS.SECCD=PARTS.SECCD) ;

Quelqu'un a une idée ?

Était-ce utile?

La solution

J'ai bien peur de ne pas pouvoir vous dire pourquoi vous n'obtenez pas d'exception, mais je peux postuler pourquoi il a choisi la version CATEGORIES de la colonne plutôt que la version PARTS.

D'après ce que j'ai compris, dans le cas de jointures à gauche, la table "principale" de la requête (PARTS) a la priorité en cas d'ambiguïté.

Il n'est pas clair si par « principale » vous entendez simplement la table de gauche dans une jointure gauche, ou la table « pilotante », comme vous voyez la requête conceptuellement...Mais dans les deux cas, ce que vous voyez comme la table « principale » dans la requête telle que vous l’avez écrite ne sera pas nécessairement la table « principale » dans l’exécution réelle de cette requête.

Je suppose qu'Oracle utilise simplement la colonne de la première table qu'il atteint lors de l'exécution de la requête.Et comme la plupart des opérations individuelles dans SQL ne nécessitent pas qu'une table soit atteinte avant l'autre, le SGBD décidera au moment de l'analyse quelle est la plus efficace à analyser en premier.Essayez d'obtenir un plan d'exécution pour la requête.Je soupçonne que cela peut révéler qu'il frappe d'abord les CATÉGORIES, puis les PIÈCES.

Autres conseils

Voici la requête (version simplifiée)

Je pense qu'en simplifiant la requête, vous avez supprimé la véritable cause du bug :-)

Quelle version d'Oracle utilisez-vous ?Oracle 10g ( 10.2.0.1.0 ) donne :

create table parts (ptno number , ptnm number , catcd number);  
create table CATEGORIES (catcd number);

select PTNO,PTNM,CATCD from PARTS  
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD);

J'obtiens ORA-00918 :colonne définie de manière ambiguë

Intéressant pour le serveur SQL qui génère une erreur (comme il se doit)

select id
from sysobjects s
left join syscolumns c on s.id = c.id

Serveur:MSG 209, niveau 16, état 1, ligne 1 nom de colonne ambiguë «id».

select id
from sysobjects 
left join syscolumns  on sysobjects.id = syscolumns.id

Serveur:MSG 209, niveau 16, état 1, ligne 1 nom de colonne ambiguë «id».

D'après mon expérience, si vous créez une requête comme celle-ci, le résultat des données extraira le CATCD du côté droit de la jointure et non de la gauche lorsqu'il y a un chevauchement de champs comme celui-ci.

Ainsi, puisque cette jointure contiendra tous les enregistrements de PARTS avec seulement quelques extraits de CATEGORIES, vous aurez NULL dans le champ CATCD chaque fois qu'il n'y aura pas de données sur le côté droit.

En définissant explicitement la colonne à partir de PARTS (c'est-à-dire côté gauche), vous obtiendrez une valeur non nulle en supposant que le champ contient des données dans PARTS.

N'oubliez pas qu'avec LEFT JOIN, vous n'avez la garantie que des données dans les champs du tableau de gauche, il peut très bien y avoir des colonnes vides à droite.

Il s'agit peut-être d'un bug de l'optimiseur Oracle.Je peux reproduire le même comportement sur la requête avec 3 tables.Intuitivement, il semble que cela devrait produire une erreur.Si je le réécris de l'une des manières suivantes, cela génère une erreur :

(1) Utilisation d'une jointure externe à l'ancienne

select ptno, catcd
from parts, categories, sections
where categories.catcd (+) = parts.catcd
  and sections.seccd (+) = parts.seccd

(2) Isoler explicitement les deux jointures

select ptno, catcd
from (
  select ptno, seccd, catcd
  from parts
  left join categories on (categories.CATCD=parts.CATCD) 
)
left join sections on (sections.SECCD=parts.SECCD)

J'ai utilisé DBMS_XPLAN pour obtenir des détails sur l'exécution de la requête, ce qui a montré quelque chose d'intéressant.Le plan consiste essentiellement à joindre extérieurement PARTS et CATEGORIES, à projeter cet ensemble de résultats, puis à le joindre extérieurement à SECTIONS.La partie intéressante est que dans la projection de la première jointure externe, elle inclut uniquement PTNO et SECCD - elle n'inclut PAS le CATCD d'aucune des deux premières tables.Par conséquent, le résultat final obtient CATCD à partir de la troisième table.

Mais je ne sais pas si c'est une cause ou un effet.

J'utilise Oracle 9.2.0.8.0.et cela donne l'erreur "ORA-00918 :colonne définie de manière ambiguë".

Il s'agit d'un bug connu avec certaines versions d'Oracle lors de l'utilisation de jointures de style ANSI.Le comportement correct serait d'obtenir une erreur ORA-00918.

Il est toujours préférable de spécifier les noms de vos tables de toute façon ;de cette façon, vos requêtes ne s'interrompent pas lorsque vous ajoutez une nouvelle colonne avec un nom qui est également utilisé dans une autre table.

Il est généralement conseillé d'être précis et de qualifier complètement tous les noms de colonnes, car cela évite un peu de travail à l'optimiseur.Certainement dans SQL Server.

D'après ce que je peux glaner du Documents Oracle, il semble que cela ne sera lancé que si vous sélectionnez le nom de la colonne deux fois dans la liste de sélection, ou une fois dans la liste de sélection, puis à nouveau ailleurs, comme une clause order by.

Peut-être avez-vous découvert une « fonctionnalité non documentée » :)

Comme HollyStyles, je ne trouve rien dans la documentation Oracle qui puisse expliquer ce que vous voyez.

PostgreSQL, DB2, MySQL et MSSQL refusent tous d'exécuter la première requête, car elle est ambiguë.

@Tapoter:J'obtiens la même erreur ici pour votre requête.Ma requête est juste un peu plus compliquée que ce que j'avais initialement publié.Je travaille maintenant sur un exemple simple reproductible.

Une question plus importante que vous devriez vous poser est la suivante : pourquoi ai-je un code de catégorie dans la table des pièces qui n'existe pas dans la table des catégories ?

Il s'agit d'un bug dans Oracle 9i.Si vous joignez plus de 2 tables en utilisant la notation ANSI, il ne détectera pas les ambiguïtés dans les noms de colonnes et pourra renvoyer la mauvaise colonne si aucun alias n'est utilisé.

Comme cela a déjà été mentionné, cela est corrigé en 10g, donc si aucun alias n'est utilisé, une erreur sera renvoyée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top