標籤:
我們在做SqlServer的查詢調優的時候,經常會在語句末尾用到option(loop/merge/hash join)或在join語句前直接聲明loop/merge/hash,來強制SqlServer使用某一特定類型的join方式。但是有些時候經過查詢最佳化工具最佳化後的執行計畫可能會和你聲明的join方式產生衝突,導致執行計畫產生失敗,我們來看一下下面這個典型案例。
declare @id1 int=1,@id2 int=1select * from [dbo].[T_People] a inner join [dbo].[T_People_II] b on a.id=b.idwherea.id=@id1 and b.id=@id2option(hash join)
我們在上面的查詢語句中使用了option提示符強制讓SqlServer使用Hash Join來做join查詢,在執行該語句時我們會得到如下錯誤資訊:
訊息 8622,層級 16,狀態 1,第 3 行
由於此查詢中定義了提示,查詢處理器未能產生查詢計劃。請重新提交查詢,並且不要在查詢中指定任何提示,也不要使用 SET FORCEPLAN。
為什麼SqlServer會產生這麼一個資訊呢?
主要是因為查詢最佳化工具在分析上面這個語句的時候發現了inner join的條件是
a.id=b.id
然後又在where條件中發現了
[email protected] and [email protected]
那麼在查詢最佳化工具看來,@id1和@id2可能為任何int類型的值,甚至有可能@[email protected],比如@[email protected]=1
那麼這個時候查詢最佳化工具會認為inner join完全是多餘的,所以上面的語句就被查詢最佳化工具最佳化為了cross join的方式,如下方式,然後在這個結果上再做了a.id=1 and b.id=1的過濾
select * from [dbo].[T_Peopl] a cross join [dbo].[T_People_II] b
這個時候你去觀察上面這個語句的查詢計劃,發現在Nested Loops有一個紅色的小叉,表示這個join是沒有Predicate的,也就是沒有join條件的(Cross Join就沒有條件),術語叫非equijoin,實際上下面這個執行計畫中join兩邊的索引,只要有一邊沒有Predicate,那麼該join都是非equijoin。
而非equijoin是不能使用Merge/Hash join的,換句話說cross join是不能用Merge/Hash join的,只能用Loop join,所以最上面的那條語句會報錯,要求去掉option提示符。所以SqlServer的提示符不是什麼時候都可以用的,甚至with(index(...))這種強制使用索引的提示符也不能隨便亂用,因為執行計畫中的某些步驟可能會和你聲明的提示符產生衝突,從而導致執行計畫產生失敗而報錯,要根據具體情況具體分析合理使用各種SqlServer的提示符。
SqlServer 中使用Join提示的時候需要注意的一問題