1. 程式人生 > >關於T-SQL中exists或者not exists子查詢的“偽優化”的做法

關於T-SQL中exists或者not exists子查詢的“偽優化”的做法

png tro ges width 9.png 當前 color 盡心 alt

問題起源

在使用t-sql中的exists(或者not exists)子查詢的時候,不知道什麽時候開始,發現一小部分人存在一種“偽優化”的一些做法,
並且向不明真相的群眾傳遞這一種寫法“優越性”,實在看不下去,
無法傳遞給他人正確的指導思想無可厚非,給他人傳遞錯誤的思想或者說誤導人倒是一種罪惡。
本來這個事情是不值得一提的,看到越來越多被誤導的群眾開始推崇這種做法(甚至開始堅信了),實在是看不習慣,不吐不快。
典型的問題如下
select * from TableA a
where exists
(select 1 from TableB b where a.Id = b.Id ),當然這裏子查詢裏寫成select * 也無所謂。

這個要表達的邏輯就是說B表中存在與A表相同的Id的數據,就成立,這是要表達的邏輯。
參考如下寫法,有人偏偏在在exists子查詢中加上top 1 1,問其原因,為什麽提高性能?理由就是加了top 1 1,只要在TableB中存在一條滿足條件的條件即可,同時不用返回所有列,因此可以提高性能。
select * from TableA a
where exists
(select top 1 1 from TableB b where a.Id = b.Id )
與直接寫select 1 from TableB where a.Id =b.Id相比,真的可以提高性能嗎?

  exists(或者not exists)子查詢的實現是一種半連接的“探測”邏輯機制(Semi Join),意思就是只要存在(而不關心具體有多少條)符合條件的數據即可,當然是不會再B表中找到所有的數據行(或者列)之後再返回。

  但是exists(或者not exists)具體在執行的時候,到底走不走Semi Join不一定,跟具體的執行計劃有關,本文暫不討論走不走Semi Join的問題,只討論子查詢中select top 1 1 的寫法到底影不影響效率。

測試驗證

就以AdventureWorks2012示例庫的兩個表做demo,看看兩者的執行計劃和IO信息,會發現子查詢中加不加 top 1,執行計劃一樣,IO一樣,扯什麽性能……

技術分享

技術分享

  exists(或者not exists)的半連接的邏輯機制(Semi Join)決定了,你寫不寫top 1,它都是找到一個符合條件的數據之後就返回外層查詢,

  甚至在子查詢中寫select * from TableName,如果走Semi Join的執行方式,他照樣是探測到有一條存在的數據之後就返回,肯定不會把所有的行都給找出來再返回,
  以下截圖可以看到,即便子查詢是select * ,IO信息也是一樣的(當然執行計劃也一樣)。
  在當前這種情況下,可以認為exists子查詢中的*,也是不會影響效率什麽的。

技術分享

  甚至是可以在子查詢中select一個常量,也不會影響到效率或者說改變執行計劃。

  技術分享

總結

  這個問題比較簡答,當然這個場景也僅限於sqlserver中的exists或者not exists子查詢,對於別的數據庫也不確定是不是優化器內部會自動優化,當前這種場景下,對於sqlserver來說,不要費盡心思去刻意用select top 1 1去“優化”了。

關於T-SQL中exists或者not exists子查詢的“偽優化”的做法