SQL 服务器 - SQL - 连续"ON"语句



当我在数据库的旧视图中清理一些问题时,我遇到了这个"奇怪"的加入条件:

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)

表tblUserAccounts被引用为右外联接,但它的on条件直到tblChainEmployees被引用之后才被声明;则在一行中存在两个连续的on语句。

我在互联网上找不到相关的答案,因为我不知道这种join叫什么。

所以问题是:

  1. 这种"延迟条件"联接有名称吗
  2. 如果上的语句不连续,如何重写以生成相同的结果集
  3. 也许这是一个"聪明"的解决方案,而总是有一个更简单/更清晰的方法

(1(这只是语法,我从未听说过什么特殊的名称。如果您仔细阅读MSDN的这篇文章,就会发现(LEFT|RIGHT) JOIN必须与ON语句配对。如果不是,则内部的表达式被解析为<table_source>。你可以放括号使它更可读:

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(我更喜欢LEFT语法,带有明确的括号(我知道,这是一个品味问题(。这产生了相同的执行计划:

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(是的,它很聪明,就像面试中的一些C++表达式(即(i++)*(*t)[0]<<p->a(一样。语言是灵活的。表达式和查询可能很棘手,但有些"排列"会导致可读性下降和错误。

在我看来,你有tblEmail和tblPerson,它们有自己独立的ID,emailID和ID(person(,一个链接表tblPersonEmail,它有有效的emailID/ID对,然后person表可能与UserAccount有1-1关系,然后UserAccount可能与chainEmployee有1-1关系。所以为了摆脱RIGHT OUTER JOIN而支持LEFT,我会使用:

    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

我想不出一个很好的实际例子,所以我会给你一个有意义的通用例子。不幸的是,我也不知道这个通用名称。

许多人会从这样的查询开始:

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

查看结果后会发现,他们确实需要消除B中没有相应C的行,但如果你试图说where b.id2 is not null and c.id2 is not null,你就破坏了a的左联接的全部目的。

所以接下来你尝试这样做,但很快就会发现它不会起作用。链末端的内部联接基本上将两个联接都转换为内部联接。

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

这个问题看起来很简单,但却不能正常工作。本质上,在您思考了一段时间后,您发现需要控制联接顺序并首先执行内部联接。因此,下面的三个查询是实现这一点的等效方法。第一个可能是你更熟悉的:

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

你的问题有点复杂,但最终还是出现了同样的问题,这就是奇怪的东西的来源。SQL已经发展了,你必须记住,平台并不总是有派生表、标量子查询、CTE之类的花哨的东西,所以有时人们不得不这样写。还有一些图形查询生成器在旧版本的工具(如Crystal Report(中有很多限制,不允许复杂的连接条件。。。

最新更新