簡體   English   中英

什么是更好的 - 選擇TOP(1)或INNER JOIN?

[英]What is better - SELECT TOP (1) or INNER JOIN?

假設我有以下查詢:

SELECT Id, Name, ForeignKeyId, 
(SELECT TOP (1) FtName FROM ForeignTable WHERE FtId = ForeignKeyId) 
FROM Table

如果使用JOIN編寫該查詢會更快執行:

SELECT Id, Name, ForeignKeyId, FtName
FROM Table t
LEFT OUTER JOIN ForeignTable ft
ON ft.FtId = t.ForeignTableIf

好奇......而且,如果JOIN更快,在所有情況下都會更快(有很多列,大量行的表)?

編輯:我寫的查詢僅用於說明TOP(1)與JOIN的概念。 是的 - 我知道SQL Server中的查詢執行計划,但我不打算優化單個查詢 - 我試圖理解SELECT TOP(1)與JOIN背后是否存在某些理論,以及是否因為速度而選擇某種方法(不是因為個人偏好或可讀性)。

編輯2:我要感謝Aaron的詳細答案,並鼓勵人們檢查他在答案中提到的公司的SQL Sentry Plan Explorer免費工具。

最初,我寫道:

查詢的第一個版本對我來說不太可讀。 特別是因為您不需要在相關子查詢中對匹配列進行別名。 JOIN 清晰。

我仍然相信並支持這些陳述,但我想根據添加到問題中的新信息添加我原來的回復。 你問過,是否存在關於什么表現更好的一般規則或理論,TOP(1)或JOIN,將可讀性和偏好放在一邊)? 我會重新陳述,因為我評論說不,沒有一般規則或理論。 當你有一個具體的例子時,很容易證明什么更好。 讓我們看看這兩個類似於你的查詢,但它們針對我們都可以驗證的系統對象運行:

-- query 1:

SELECT name,
   (SELECT TOP (1) [object_id] 
        FROM sys.all_sql_modules 
        WHERE [object_id] = o.[object_id]
   )
FROM sys.all_objects AS o;

-- query 2:

SELECT o.name, m.[object_id]
    FROM sys.all_objects AS o
    LEFT OUTER JOIN sys.all_sql_modules AS m
    ON o.[object_id] = m.[object_id];

這些返回完全相同的結果(我的系統上有3,179行),但是我指的是相同的數據和相同的行數。 他們不是真正相同的查詢(或者至少不遵循相同的執行計划)的一個線索是結果以不同的順序返回。 雖然我不希望維護或遵守某個命令,因為我沒有在任何地方包含ORDER BY ,我希望SQL Server選擇相同的順序,如果它們實際上是使用相同的計划。

但他們不是。 我們可以通過檢查計划並進行比較來看到這一點。 在這種情況下,我將使用SQL Sentry Plan Explorer ,這是我公司的免費執行計划分析工具 - 您可以從Management Studio獲取一些此類信息,但其他部分在Plan Explorer中更容易獲得(例如實際持續時間)和CPU)。 頂部計划是子查詢版本,底部是連接。 同樣,子查詢位於頂部,連接位於底部:

子查詢版本的執行計划

[ 點擊查看完整尺寸 ]

加入版本的“執行計划”選項卡

[ 點擊查看完整尺寸 ]

實際執行計划:運行兩個查詢的總成本的85%是在子查詢版本中。 這意味着它的價格是加入價格的5倍多。 子查詢版本的CPU和I / O都要高得多 - 看看所有這些讀取! 6,600多頁返回~3,000行,而連接版本使用少得多的I / O返回數據 - 僅110頁。

但為什么? 因為子查詢版本的工作方式基本上類似於標量函數,您可以從另一個表中獲取TOP匹配行,但是對於原始查詢中的每一行都要執行此操作。 我們可以通過查看Top Operations選項卡看到該操作發生了3,179次,該選項卡顯示了每個操作的執行次數。 再次,更昂貴的子查詢版本位於頂部,加入版本如下:

子查詢版本的“頂級操作”選項卡

加入版本的“最佳操作”選項卡

我將為您提供更徹底的分析,但總的來說,優化器知道它在做什么。 陳述你的意圖(這些表之間的這種類型的連接)和99%的時間它將自己解決什么是最好的基本方式(例如執行計划)。 如果你試圖超越優化器,請記住,你正在冒險進入相當先進的領域。

每個規則都有例外,但在這種特定情況下,子查詢肯定是個壞主意。 這是否意味着第一個查詢中提出的語法總是一個壞主意? 絕對不。 可能存在一些模糊的情況,其中子查詢版本與連接一樣有效。 我不能認為有很多子查詢可以更好地工作。 因此,我會更傾向於那個更有可能更好或更好的那個以及更具可讀性的那個。 我看不出有什么優勢,子查詢的版本,即使你找到它更具有可讀性,因為它是最有可能會導致性能

一般來說,我強烈建議您堅持使用更具可讀性的自我文檔語法,除非您發現優化器沒有正確執行的情況(我敢打賭99%的情況下,問題是錯誤的統計信息或參數嗅探,而不是查詢語法問題)。 我懷疑,除了這些情況之外,你可以重現哪些復制品,哪些復雜的查詢比它們更直接和邏輯等價的工作更好,這種情況很少見。 您嘗試查找這些案例的動機應與您對普遍接受的“最佳實踐”語法的非直覺語法的偏好大致相同。

您的查詢做了不同的事情。 第一種更類似於LEFT OUTER JOIN。

這取決於您的索引如何設置性能。 但是JOIN更清楚。

  1. 我同意上面的陳述(瑞克)。 在執行計划中運行此...您將得到一個明確的答案。 不需要猜測。

  2. 我同意Daniel和Davide的觀點,這些是兩種不同的SQL語句。 如果ForeignTable有多個具有相同FtId值的記錄,那么您將獲得重復的數據。 假設第一個SQL語句是正確的,您將不得不用一些GROUP BY子句重寫第二個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM