2013-11-01 61 views
6

我有一個四表,TopLevelParent,兩個中級表MidParentA和MidParentB,以及一個子表,它可以有一個MidParentA或MidParentB的父級(一個或另一個midParentB必須在地點)。兩個中級表都有一個TopLevelParent的父表。SQL Server左加入'或'運營商

頂級表是這樣的:

TopLevelId | Name 
-------------------------- 
1   | name1 
2   | name2 

的中親表是這樣的:

MidParentAId | TopLevelParentId |   MidParentBId | TopLevelParentId | 
------------------------------------  ------------------------------------ 
1   |  1   |   1   |  1   | 
2   |  1   |   2   |  1   | 

子表是這樣的:

ChildId | MidParentAId | MidParentBId 
-------------------------------- 
1  |  1  | NULL 
2  | NULL  |  2 

我已經使用接下來的左邊加入一個更大的存儲過程,它正在超時,它看起來像是OR操作r最後一個加入是罪魁禍首:

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 

是否有更高性能的方式來做這個連接?

回答

2

這裏的演示是我的結束,這得到了執行時間從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(即IsMidParentA和IsMidParentB的ID爲1)。

感謝所有花時間回答的人。

+3

這是一些複雜的商業 – Roel

2

你應該照顧在On中使用謂詞。

「理解這一點非常重要,因爲使用外連接,ON和WHERE子句扮演着非常不同的角色,因此它們不可互換。WHERE子句仍然起着簡單的過濾作用 - 即它保持如果使用類似的東西並且在where子句中使用謂詞,那麼ON子句並不扮演一個簡單的過濾角色,而是更匹配一個角色,換句話說,不管ON謂詞是否與它匹配,ON謂詞都會返回,因此ON謂詞只能確定非保留端的哪些行與保留端的行匹配,而不是是否返回保留端的行。 **考試70-461:查詢Microsoft SQL Server 2012

9

鑑於查詢的暴露程度如何;一個非常粗略的經驗法則是用一個聯盟替換一個Or來避免表掃描。

Select.. 
LEFT JOIN Child c ON c.ParentAId = a.ParentAId 
union 
Select.. 
left Join Child c ON c.ParentBId = b.ParentBId 
+0

我編輯了這個問題來顯示更多的查詢 –

+1

這有助於:)你可以有兩個連接到Child,而不是每個做OR的一邊;然後使用coalesce從C或D中獲取值,您只需要一個值 – u07ch

0

另一種方式來寫它:

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 

SQLFiddle

+0

原始查詢具有c.ParentBId = b.ParentBId而不是c.parentAID = b.parentBID,因此這可能不正確 – u07ch

+0

您是對的,乍一看我沒有仔細閱讀這個問題 – mucio