当我在数据库的旧视图中清理一些问题时,我遇到了这个"奇怪"的加入条件:
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(这只是语法,我从未听说过什么特殊的名称。如果您仔细阅读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(中有很多限制,不允许复杂的连接条件。。。