2009-07-05 50 views
57

我想知道是否有人可以幫助提高我對SQL中JOIN的理解。 [如果問題很重要,我特意考慮MS SQL Server。]瞭解JOIN在涉及3個或更多表時如何工作。 [SQL]

取3個表A,B [與某些A.AId相關的A],以及C [B與某個B有關的C .BId]

如果我編寫一個查詢如

SELECT * 
FROM A JOIN B 
ON A.AId = B.AId 

都好 - 我是甜中帶是如何工作的。

當表C(或其他d,E,......被添加),會發生什麼

的情況

SELECT * 
FROM A JOIN B 
    ON A.AId = B.AId 
JOIN C ON C.BId = B.BId 

什麼是C加入到? - 它是B表(和B表中的值嗎?) 或者它是C表加入的A + B連接的結果的某個其他臨時結果集?

[言下之意是不是在B表中的所有值必然是在基於用於連接條件的臨時結果集A + B,B]

的具體(和相當人爲的)例子爲什麼我問的是因爲我想了解的行爲,我在下面看到:

Tables 
Account (AccountId, AccountBalanceDate, OpeningBalanceId, ClosingBalanceId) 
Balance (BalanceId) 
BalanceToken (BalanceId, TokenAmount) 

Where: 
Account->Opening, and Closing Balances are NULLABLE 
(may have opening balance, closing balance, or none) 

Balance->BalanceToken is 1:m - a balance could consist of many tokens 

概念,截止日期的平衡,將是明天的期初餘額

如果我試圖找到所有的開幕和期末餘額爲帳戶

我可能會做這樣的事情

SELECT AccountId 
, AccountBalanceDate 
, Sum (openingBalanceAmounts.TokenAmount) AS OpeningBalance 
, Sum (closingBalanceAmounts.TokenAmount) AS ClosingBalance 
FROM Account A 
    LEFT JOIN BALANCE OpeningBal 
     ON A.OpeningBalanceId = OpeningBal.BalanceId 
    LEFT JOIN BALANCE ClosingBal 
     ON A.ClosingBalanceId = ClosingBal.BalanceId 
    LEFT JOIN BalanceToken openingBalanceAmounts 
     ON openingBalanceAmounts.BalanceId = OpeningBal.BalanceId 
    LEFT JOIN BalanceToken closingBalanceAmounts 
     ON closingBalanceAmounts.BalanceId = ClosingBal.BalanceId 
    GROUP BY AccountId, AccountBalanceDate 

事情工作,我所期望的,直到最後JOIN帶來期末餘額令牌 - 在我結束了在結果重複。

[我可以用DISTINCT解決 - 但我想知道爲什麼發生了什麼正在發生的事情]

有人告訴我這個問題是因爲平衡之間的關係,並BalanceToken是1:M - 和當我引入最後一個JOIN時,我得到了重複,因爲第三個JOIN已經多次將BalanceIds引入(我假設)臨時結果集中。

我知道,例如表不符合良好的DB設計

道歉的文章,感謝您的任何elightenment :)響應

編輯馬克·

概念爲質疑賬戶裏不應該有重複賬戶的BalanceToken(根據AccountingDate) - 我認爲問題的關鍵是因爲1賬戶/會計賬戶餘額結餘是賬戶餘額在第二天 - 所以當自我加入餘額,BalanceToken多次到獲得期初和期末餘額我認爲B alances(BalanceId)被多次納入'結果組合'。如果有助於澄清第二個例子,可以將其視爲每日對帳 - 因此,剩下的加入 - 可能沒有爲給定的賬戶/會計日期組合計算開放(和/或)結賬餘額。

+12

+1詳細的問題和自己的推理。 – PatrikAkerstrand 2009-07-05 08:30:26

+1

小小的OT,但值得一提:http://www.codeproject.com/KB/database/Visual_SQL_Joins.aspx – 2009-07-05 09:13:28

回答

30

概念這裏是當你將三個表連接在一起時發生的情況。

  1. 優化器提出了一個計劃,其中包含一個連接順序。它可以是A,B,C或C,B,A或任何組合
  2. 查詢執行引擎將任何謂詞(0​​子句)應用於不涉及任何其他表的第一個表。它會選擇JOIN條件或SELECT列表或ORDER BY列表中提及的列。稱此結果爲A
  3. 它將此結果集連接到第二個表。對於每行都加入到第二個表中,應用可能應用於第二個表的任何謂詞。這導致另一個臨時結果集。
  4. 然後加入在決賽桌,並應用ORDER BY

這在概念上發生了什麼。事實上,沿途有許多可能的優化。關係模型的優點在於,良好的數學基礎使得計劃的各種轉換成爲可能,而不會改變正確性。

例如,真的沒有必要一路生成完整的結果集。 ORDER BY可以改爲通過首先使用索引訪問數據來完成。有很多類型的連接也可以完成。

5

我們知道B的數據將通過(內部)連接過濾到AA中的數據也被過濾)。因此,如果我們(內部)從B加入到C,因此集C過濾關係到A。並且還要注意,來自加入的任何重複將包括

但是;這種情況發生的順序取決於優化器;它可能會決定先執行B/C加入,然後再引入A或任何其他序列(可能基於每個連接的估計行數和相應索引)。


然而,在你以後的例子中你使用了一個LEFT OUTER連接;所以Account沒有被過濾在所有,並且如果任何其他表具有多個匹配,可能我的重複。

BalanceToken中是否有重複項(每個帳戶)?

+0

嗨馬克 - 感謝您的迴應,我編輯了最初的問題與信息迴應你的問題 – Delaney 2009-07-05 08:52:43

1

我經常發現它有助於查看實際執行計劃。在查詢分析器/管理工作室中,您可以打開查詢菜單中的查詢或使用Ctrl + M。運行查詢後,執行的計劃顯示在另一個結果選項卡中。從這裏你可以看到C和B首先被連接,然後結果和A連接在一起。這個計劃可能會因DBMS的信息而有所不同,因爲這兩個連接都是內部的,使得它成爲A和B和C 。我的意思是,無論首先加入哪個結果,結果都是一樣的,但所花的時間可能差別很大,這就是優化器和提示發揮作用的地方。

1

連接可能會非常棘手,並且大部分行爲當然都由數據如何存儲在實際表中來決定。

在沒有看到表格的情況下,很難在特定情況下給出明確的答案,但我認爲基本問題是您正在將多個結果集合在一起進行求和。

也許而不是多個聯接,您應該在查詢中創建兩個單獨的臨時表,一個包含accountsID,日期和balancebalances總和,第二個包含accountID,日期和期末餘額總和,然後將這兩個臨時表加入帳戶ID和日期。

爲了找出到底發生了什麼事與連接,也是在特定情況下,我會做到以下幾點:

更改初始部分

SELECT帳戶ID Accountbalancedate,SUM(...)作爲openingbalance, 總和(...),因爲從年末數

簡單

「SELECT * FROM」

研究生成的表格,您將看到正確的數據被複制。逐個刪除連接,看看會發生什麼。這應該給你一個線索,說明你的特定數據是什麼造成了這些模糊。

如果您在SQL server management studio中打開查詢(存在免費版本),則可以在設計器中編輯該查詢。如何加入表格的可視化視圖還可以幫助您瞭解發生了什麼。