Join的表順序


在今天的文章里,我想談下SQL Server里一個非常有趣的話題:在表聯接里,把表指定順序的話是否有意義?每次我進行查詢和性能調優的展示時,大家都會問我他們是否應該把聯接中的表指定下順序,是否會幫助查詢優化器得出一個更好性能的執行計划。我們來看下這個重要又有趣的問題。

合並聯接(Inner Joins)

假設在AdventureWorks數據庫里,你要在Sales.SalesOrderHeader表和Sales.SalesOrderDetail表之間做一個內聯接:

 1 USE AdventureWorks
 2 GO
 3 
 4 -- Returns for each SalesOrderHeader record all associated SalesOrderDetail records
 5 -- SQL Server performs a Merge Join, because both tables are phyiscally sorted
 6 -- by the column "SalesOrderID".
 7 SELECT
 8     h.SalesOrderID,
 9     h.CustomerID,
10     d.SalesOrderDetailID,
11     d.ProductID,
12     d.LineTotal
13 FROM Sales.SalesOrderHeader h
14 JOIN Sales.SalesOrderDetail d
15 ON h.SalesOrderID = d.SalesOrderID
16 ORDER BY SalesOrderID
17 GO

當我們查看結果的執行計划時,我們可以看到查詢優化器選擇了合並聯接(Inner Join)作為物理聯接運算符,Sales.SalesOrderHeader表作為合並聯接的外聯接。在執行計划里表的順序和我們在邏輯T-SQL查詢里的順序是一樣的。

現在的問題是,當我們在邏輯T-SQL查詢里交換下2個表的順序,執行計划會發生什么?我們來試下:

 1 -- The logical ordering of the tables during an Inner Join
 2 -- doesn't matter. It's up to the Query Optimnizer to arrange
 3 -- the tables in the best order.
 4 -- This query produces the same execution plan as the previous one.
 5 SELECT
 6     h.SalesOrderID,
 7     h.CustomerID,
 8     d.SalesOrderDetailID,
 9     d.ProductID,
10     d.LineTotal
11 FROM Sales.SalesOrderDetail d
12 JOIN Sales.SalesOrderHeader h
13 ON d.SalesOrderID = h.SalesOrderID
14 ORDER BY SalesOrderID
15 GO

但我們現在看結果的執行計划,我們發現很有意思:

在執行計划里沒有任何改變!查詢優化器選擇了和剛才查詢一樣的物理執行計划。但為什么?答案非常簡單:查詢優化器總引用最小的表(基於我們的統計信息!)作為每個物理連接運算符(嵌套循環聯接,合並聯接,哈希匹配聯接)的外聯接表。因此在T-SQL查詢里的表的邏輯順序不會對查詢優化器造成任何影響。按正確的順序訪問我們的表是查詢優化器的職責。

在表A和表B之間的合並聯接與表B和表A之間的合並聯接是一樣的。

外聯接(Outer Join)

在外聯接(left join,right join)里,表順序會有啥影響?我們來看下面的查詢,在Sales.Customer表和 Sales.SalesOrderHeader表之間進行左聯接。

 1 -- Execute the query with an Outer Join.
 2 -- Now we are also getting back customers that haven't placed orders.
 3 -- The left table is the preserving one, and missing rows from the right table are added with NULL values.
 4 -- SQL Server performs a "Merge Join (Left Outer Join)" in the execution plan.
 5 SELECT
 6     c.CustomerID,
 7     h.SalesOrderID
 8 FROM Sales.Customer c
 9 LEFT JOIN Sales.SalesOrderHeader h
10 ON c.CustomerID = h.CustomerID
11 GO

當我們查看結果執行計划時,我們會看到查詢優化器已經隱藏了我們的表順序。

當然這次我們不能修改T-SQL語句里的表順序,不然查詢會返回錯誤的結果。但當我們在查詢里切換下表會發生什么,不是左聯接,我們用右聯接。我們來試下:

1 -- You can rewrite the query from above with a Right Outer Join when you swap the order
2 -- of the tables. This time you get back the same result (32166 rows).
3 SELECT
4     c.CustomerID,
5     h.SalesOrderID
6 FROM Sales.SalesOrderHeader h
7 RIGHT JOIN Sales.Customer c
8 ON c.CustomerID = h.CustomerID
9 GO

當我們看執行計划時,我們再次看到沒有任何改變:查詢優化器轉化右聯接為左聯接,重排了下表還是返回正確的結果。查詢優化器的目標是使用最小表作為物理聯接運算符的外表。因此在外聯接里表的順序也不會影響查詢優化器。只要我們的統計信息是正確的,查詢優化器總會選擇正確的順序。

在表A和表B之間的左聯接與表B和表A之間的右聯接是一樣的。

小結:

在這篇文章里我們討論對於聯接,表的順序是否會影響執行計划。如我們所見,這完全由查詢優化器來決定選擇優化的表順序——基於統計信息。在合並聯接里表順序完全不影響,使用外聯接的話,SQL Server可以通過切換左聯接/右聯接來重排表,還是獲得正確的結果。

參考文章:

http://www.sqlpassion.at/archive/2015/12/15/table-ordering-for-joins/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM