SQL – Consécutive "ON" Déclarations

Comme je nettoyais quelques problèmes dans une ancienne vue dans notre database, je suis tombé sur cette condition de jointure "étrange":

from tblEmails [e] join tblPersonEmails [pe] on (e.EmailID = pe.EmailID) right outer join tblUserAccounts [ua] join People [p] on (ua.PersonID = p.Id) join tblChainEmployees [ce] on (ua.PersonID = ce.PersonID) on (pe.PersonID = p.Id) 

La table tblUserAccounts est référencée en tant que jointure externe droite , mais la condition d'activation n'est pas déclarée tant que tblChainEmployees n'est pas référencé; puis il y a deux instructions consécutives sur une ligne.

Je n'ai pas trouvé de réponse pertinente nulle part sur Internet, car je ne savais pas à quoi s'appelait ce type de jointure .

Donc les questions:

  1. Est-ce que ce genre de jointure "différée différée" a un nom?
  2. Comment cela peut-il être réécrit pour produire le même set de résultats où les instructions on ne sont pas consécutives?
  3. Peut-être que c'est une solution "intelligente" quand il y a toujours eu une manière plus simple / plus claire?

(1) C'est juste de la syntaxe et je n'ai jamais entendu parler d'un nom particulier. Si vous lisez attentivement cet article MSDN, vous verrez que (LEFT|RIGHT) JOIN doit être associé à l'instruction ON . Si ce n'est pas le cas, expression à l'intérieur est analysée comme <table_source> . Vous pouvez mettre des parenthèses pour le rendre plus lisible:

 from tblEmails [e] join tblPersonEmails [pe] on (e.EmailID = pe.EmailID) right outer join ( tblUserAccounts [ua] join People [p] on (ua.PersonID = p.Id) join tblChainEmployees [ce] on (ua.PersonID = ce.PersonID) ) on (pe.PersonID = p.Id) 

(2) Je préférerais la syntaxe LEFT , avec des parenthèses explicites (je sais, c'est une question de goût). Cela produit le même plan d'exécution:

 FROM tblUserAccounts ua JOIN People p ON ua.PersonID = p.Id JOIN tblChainEmployees ce ON ua.PersonID = ce.PersonID LEFT JOIN ( tblEmails e JOIN tblPersonEmails pe ON e.EmailID = pe.EmailID ) ON pe.PersonID = p.Id 

(3) Oui, c'est intelligent, comme certaines expressions C ++ (ie (i++)*(*t)[0]<<p->a ) sur les interviews. La langue est flexible. Les expressions et les requêtes peuvent être difficiles, mais certaines «dispositions» conduisent à une dégradation de la lisibilité et à des erreurs.

On dirait que vous avez tblEmail et tblPerson avec leurs propres ID indépendants, emailID et ID (personne), une table de binding tblPersonEmail avec les paires valides de emailID / IDs, puis la table de personnes peut avoir une relation 1-1 avec UserAccount, qui peut alors avoir une relation 1-1 avec chainEmployee, donc pour se débarrasser de la RIGHT OUTER JOIN au profit de LEFT, j'utiliserais:

  FROM ((tblPerson AS p INNER JOIN (tblEmail AS e INNER JOIN tblPersonEmail AS pe ON e.emailID = pe.emailID) ON p.ID = pe.personID) LEFT JOIN tblUserAccount AS ua ON p.ID = ua.personID) LEFT JOIN tblChainEmployee AS ce ON ua.personID = ce.personID 

Je ne peux pas penser à un grand exemple concret de ceci au sumt de ma tête ainsi je vous donnerai un exemple générique qui, je l'espère, a du sens. Malheureusement, je ne connais pas de nom générique pour cela non plus.

Beaucoup de gens vont commencer avec une requête comme celle-ci:

 select ... from A a left outer join B b on b.id = a.id left outer join C c on c.id2 = b.id2; 

Le regard sur les résultats et se rendre count qu'ils ont vraiment besoin d'éliminer les lignes dans B qui n'ont pas un C correspondant, mais si vous avez essayé de dire where b.id2 is not null and c.id2 is not null vous avez vaincu le tout le but de la jointure à gauche de A.

Alors, vous essayez de le faire, mais il ne faut pas longtime pour comprendre que ça ne marchera pas. La jointure interne à la fin de la string a essentiellement converti les jointures en jointures internes.

 select ... from A a left outer join B b on b.id = a.id inner join C c on c.id2 = b.id2; 

Le problème semble simple mais cela ne fonctionne pas correctement. Essentiellement, après avoir réfléchi pendant un moment, vous découvrez que vous devez contrôler l'ordre de jointure et faire la jointure interne en premier . Donc, les trois requêtes ci-dessous sont des moyens équivalents pour y parvenir. Le premier est probablement celui que vous connaissez le mieux:

 select ... from A a left outer join (select * from B b inner join C c on c.id2 = b.id2) bc on bc.id = a.id select ... from A a left outer join B b inner join C c on c.id2 = b.id2 on b.id = a.id select ... from B b inner join C c on c.id2 = b.id2 right outer join -- now they can be done in order A a on a.id = b.id 

Vous interrogez est un peu plus compliqué mais en fin de count les mêmes problèmes sont entrés en jeu qui est d'où viennent les choses étranges. SQL a évolué et vous devez vous callbacker que les plates-forms n'avaient pas toujours les choses fantaisistes comme les tables dérivées, les sous-requêtes scalaires, les CTE, donc parfois les gens devaient écrire des choses de cette façon. Et puis il y avait des constructors de requêtes charts avec beaucoup de limitations dans les anciennes versions d'outils comme Crystal Report qui ne permettait pas les conditions de jointures complexes …