我有一个四个表,TopLevelParent,两个中级表MidParentA和MidParentB,以及一个Child表,它可以有一个父MidParentA或MidParentB(一个或另一个midParent必须到位)。两个中级表都有一个TopLevelParent的父表。
顶级表格如下所示:
TopLevelId | Name
--------------------------
1 | name1
2 | name2
MidParent表格如下所示:
MidParentAId | TopLevelParentId | MidParentBId | TopLevelParentId |
------------------------------------ ------------------------------------
1 | 1 | 1 | 1 |
2 | 1 | 2 | 1 |
Child表看起来像这样:
ChildId | MidParentAId | MidParentBId
--------------------------------
1 | 1 | NULL
2 | NULL | 2
我在一个较大的存储过程中使用了以下左连接,这是一个超时,看起来左边连接的OR运算符是罪魁祸首:
SELECT *
FROM TopLevelParent tlp
LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId
LEFT JOIN MidParentB a ON tlp.TopLevelPatientId = b.TopLevelPatientId
LEFT JOIN Child c ON c.ParentAId = a.ParentAId OR c.ParentBId = b.ParentBId
是否有更高效的方式来加入?
答案 0 :(得分:17)
鉴于查询的公开程度很小;一个非常粗略的经验法则是用联合替换Or以避免表扫描。
Select..
LEFT JOIN Child c ON c.ParentAId = a.ParentAId
union
Select..
left Join Child c ON c.ParentBId = b.ParentBId
答案 1 :(得分:5)
你应该注意在On中使用谓词。
“理解使用外连接时,非常重要的是ON和WHERE子句扮演非常不同的角色,因此它们不可互换.WHERE子句仍然扮演一个简单的过滤角色 - 即,它保持真实使用类似的东西并在where子句中使用谓词。然而,ON子句不起简单的过滤作用;相反,它更像是一个匹配的角色。换句话说,保留了一行无论ON谓词是否找到匹配,都将返回side。因此ON谓词只确定来自非保留端的哪些行与保留端的行匹配 - 而不是是否从保留端返回行。 **考试70-461:查询Microsoft SQL Server 2012
答案 2 :(得分:4)
这是我最后所做的,它将执行时间从52秒降低到4秒。
SELECT *
FROM (
SELECT tpl.*, a.MidParentAId as 'MidParentId', 1 as 'IsMidParentA'
FROM TopLevelParent tpl
INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID
UNION
SELECT tpl.*, b.MidParentBId as 'MidParentId', 0 as 'IsMidParentA'
FROM TopLevelParent tpl
INNER JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID
UNION
SELECT tpl.*, 0 as 'MidParentId', 0 as 'IsMidParentA'
FROM TopLevelParent tpl
WHERE tpl.TopLevelParentID NOT IN (
SELECT pa.TopLevelParentID
FROM TopLevelParent tpl
INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID
UNION
SELECT pa.TopLevelParentID
FROM TopLevelParent tpl
INNER JOIN MidParentB b ON h.TopLevelParentId = tpl.TopLevelParentID
)
) tpl
LEFT JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID
LEFT JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID
LEFT JOIN
(
SELECT [ChildId]
,[MidParentAId] as 'MidParentId'
,1 as 'IsMidParentA'
FROM Child c
WHERE c.MidParentAId IS NOT NULL
UNION
SELECT [ChildId]
,[MidParentBId] as 'MidParentId'
,0 as 'IsMidParentA'
FROM Child c
WHERE c.MidParentBId IS NOT NULL
) AS c
ON c.MidParentId = tpl.MidParentId AND c.IsMidParentA = tpl.IsMidParentA
这消除了正在发生的表扫描,因为如果它存在的话,我已将顶级记录与其中级父级匹配,并将其标记在该记录上。
我也对子记录做了同样的事情,这意味着我可以将子记录加入MidParentId的顶级记录,并使用IsMidParentA位标志来区分有两个相同MidParentIds的位置(即Id为IsMidParentA和IsMidParentB提供1。
感谢所有花时间回答的人。
答案 3 :(得分:1)
另一种写作方式:
<德尔> LEFT JOIN Child c ON c.ParentAId = COALESCE(a.ParentAId, b.ParentBId)
德尔>
修改强>
一种可能的方法是首先查询MidParentA,然后查询MidParentB,然后查询UNION
结果:
SELECT tlp.*,
a.MidParentAId,
null MidParentBId,
c.ChildId
FROM TopLevelParent tlp
LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId
LEFT JOIN Child c ON c.MidParentAId = a.MidParentAId
UNION
SELECT tlp.*,
null MidParentAId,
b.MidParentBId,
c.ChildId
FROM TopLevelParent tlp
LEFT JOIN MidParentB b ON tlp.TopLevelPatientId = b.TopLevelPatientId
LEFT JOIN Child c ON c.MidParentBId = b.MidParentBId
中的演示