2016-07-25 32 views
2

我正在一個大約1000萬行的大表上進行搜索。我想指定開始日期和結束日期,並返回在這些日期之間創建的表中的所有記錄。在DATETIME列上的DATETIME搜索謂詞比字符串文字謂詞慢得多

這是一個直接的查詢:

declare @StartDateTime datetime = '2016-06-21', 
     @EndDateTime datetime = '2016-06-22'; 

select * 
FROM Archive.dbo.Order O WITH (NOLOCK) 
where O.Created >= @StartDateTime 
    AND O.Created < @EndDateTime; 

創建是具有非聚集索引中的DATETIME列。

此查詢大約需要15秒才能完成。

但是,如果我稍微修改查詢,如下所示,則僅需1秒鐘返回相同的結果:

declare @StartDateTime datetime = '2016-06-21', 
     @EndDateTime datetime = '2016-06-22'; 

select * 
FROM Archive.dbo.Order O WITH (NOLOCK) 
where O.Created >= '2016-06-21' 
    AND O.Created < @EndDateTime; 

唯一的變化是用一個字符串替換@StartDateTime搜索謂詞。查看執行計劃,當我使用@StartDateTime時,它執行了索引掃描,但是當我使用字符串時,它執行了索引查找,速度提高了15倍。

有誰知道爲什麼使用字符串文字是如此之快?

我原以爲DATETIME列和DATETIME變量之間的比較比將列與日期的字符串表示進行比較要快。我嘗試刪除並重新創建創建列上的索引,它沒有任何區別。我注意到我在生產系統上得到了類似於測試系統上的結果,所以奇怪的行爲似乎並不特定於特定的數據庫或SQL Server實例。

+1

答案是關係到SQL Server如何識別查詢中的變量。如果SQL在運行查詢之前無法知道該變量是什麼,它可能無法使用緩存計劃或無法有效猜測新的計劃 –

+1

有一篇關於該主題的好文章,我認爲您會發現它有幫助:[SQLmag - 優化變量和參數](http://m.sqlmag.com/t-sql/optimizing-variables-and-parameters) –

+0

@clifton_h:對不起,它需要一段時間,一直忙於工作。您鏈接的那篇文章回答了我的問題。如果您想將您的評論轉換爲答案,我會接受它。 –

回答

1

所有變量都有它們被識別的實例。

OOP語言中,我們通常從臨時變量static/constant變量之間通過使用關鍵字區分時,或者當變量被稱爲入其中該實例內的變量被當作一個常數的函數,如果函數變換變量,像這樣在C++如下:

void string MyFunction(string& name) 
//technically, `&` calls the actual location of the variable 
//instead of using a logical representation. The concept is the same. 

SQL Server,標準選擇不同的方式實現這一點。有沒有constant數據類型,所以不是我們用文字它們要麼

  • 對象的名稱(必須在呼叫作爲系統的關鍵字類似的優先級)
  • 名稱與對象deliminator(包括」,[]
  • 或帶有分隔符的字符串CHAR(39)(')。

這是你注意到兩個查詢產生不同的結果,因爲這些變量不是常量的優化,這意味着SQL Server將已經選擇了它的執行路徑提前的原因。

如果您安裝了SSMS,請包含實際執行計劃(CTRL + M),並在select語句中註明預計行數。這是執行計劃的亮點。 「估計」行和「實際」行之間的差異越大,查詢可以使用優化的可能性就越大。在您的示例中,SQL Server必須猜測有多少行,並最終超出結果,從而降低效率。

解決方案是一樣的,但如果您願意,仍然可以封裝所有內容。我們使用AdventureWorks2012這個例子:

1)聲明變量在Procedure

CREATE PROC dbo.TEST1 (@NameStyle INT, @FirstName VARCHAR(50)) 
AS 
BEGIN 
    SELECT * 
    FROM Person.PErson 
    WHERE FirstName = @FirstName 
    AND NameStyle = @NameStyle; --namestyle is 0 
END 

2)傳遞變量爲Dynamic SQL

CREATE PROC dbo.TEST2 (@NameStyle INT) 
AS 
BEGIN 

DECLARE @Name NVARCHAR(50) = N'Ken'; 
DECLARE @String NVARCHAR(MAX) 
SET @String = 
    N'SELECT * 
    FROM Person.PErson 
    WHERE FirstName = @Other 
    AND NameStyle = @NameStyle'; 
EXEC sp_executesql @String 
      , N'@Other VARCHAR(50), @NameStyle INT' 
      , @Other = @Name 
      , @NameStyle = @NameStyle  
END 

兩個計劃會產生相同的結果。我可以用EXEC本身,而是sp_executesql可以緩存整個select語句(加,其更SQL Injection安全)

請注意,在這兩種情況下允許SQL Server的變量轉化爲一個恆定值的情況下的水平(意思它以設定的值輸入對象),然後優化器能夠選擇最有效的執行計劃。

-- Remove Procs 
DROP PROC dbo.TEST1 
DROP PROC dbo.TEST2 

一個偉大的文章強調了在OP的註釋部分,但你可以看到它在這裏:Optimizing Variables and Parameters - SQLMAG