2011-08-18 31 views
2

在我的SELECT語句中,我使用的可選參數的方式是這樣的:可選參數,「索引查找」計劃

DECLARE @p1 INT = 1 
DECLARE @p2 INT = 1 
SELECT name FROM some_table WHERE (id = @p1 OR @p1 IS NULL) AND (name = @p2 OR @p2 IS NULL) 

在這種情況下,優化器生成「索引掃描」(不求)操作的實體當參數提供非空值時,這不是最有效的。
如果我將RECOMPILE提示添加到查詢中,優化器將使用「seek」構建更有效的計劃。它適用於我的MSSQL 2008 R2 SP1服務器,這也意味着優化器可以構建一個只考慮我的查詢的一個邏輯分支的計劃。
我該如何使它能夠在任何地方使用該計劃,而不需要重新編譯?在這種情況下,USE PLAN暗示圖標不起作用。

下面是測試代碼:

-- see plans 
CREATE TABLE test_table( 
    id INT IDENTITY(1,1) NOT NULL, 
    name varchar(10),  
    CONSTRAINT [pk_test_table] PRIMARY KEY CLUSTERED (id ASC)) 
GO 
INSERT INTO test_table(name) VALUES ('a'),('b'),('c') 
GO 
DECLARE @p INT = 1 
SELECT name FROM test_table WHERE id = @p OR @p IS NULL 
SELECT name FROM test_table WHERE id = @p OR @p IS NULL OPTION(RECOMPILE) 
GO 
DROP TABLE test_table 
GO 

請注意,並非所有的SQL Server版本將改變我的計劃顯示的方式。

回答

1

您得到掃描的原因是因爲謂詞不會短路,並且總是會評估兩個語句。正如你已經指出的那樣,優化器不能很好地工作並強制掃描。儘管with recompile有時似乎有所幫助,但並不一致。

如果你有一個大表,其中的目的是必須的,那麼你有兩種選擇:

  1. 動態SQL。
  2. 如果語句分隔您的查詢,並因此創建單獨的執行計劃(當@p is null你當然總是會得到一個掃描)。
+0

謝謝你的回覆。是的,我知道其他解決方法與尋求 - 使用IFs,重構SQL等。但很遺憾,我不能提供查詢與優化器只爲這個查詢建立的計劃... – LINQ2Vodka

0

看看這篇文章http://www.bigresource.com/Tracker/Track-ms_sql-fTP7dh01/ 看來你可以嘗試使用建議的解決方案:

`SELECT * FROM <table> WHERE IsNull(column, -1) = IsNull(@value, -1)` 

`SELECT * FROM <table> WHERE COALESCE(column, -1) = COALESCE(@value, -1)` 
+1

這是一個錯誤匹配的OP邏輯。你需要'WHERE column = IsNull(@value,column)',並且這會產生非常不同的屬性。 – MatBailie

1

迴應評論Andreas的答案

問題是你需要兩個不同的計劃。

  • 如果@p1 = 1那麼你可以在索引上使用SEEK。
  • 如果@p1 IS NULL,但它不是一個尋找,根據定義它是一個掃描。

這意味着,當優化器在生成計劃之前到參數的知識,它需要創建一個可以fullfil所有可能性的計劃。只有一個掃描可以覆蓋的需求@p1 = 1@p1 IS NULL

這也意味着如果計劃在參數已知時重新編譯,並且@p1 = 1,SEEK計劃可以創建

這就是您在評論中提到的IF語句解決您的問題的原因;每個IF塊表示問題空間的不同部分,並且每個IF塊可以被賦予不同的執行計劃。