簡體   English   中英

使用日期表達式查詢運行緩慢,但使用字符串文字快速查詢

[英]Query runs slow with date expression, but fast with string literal

我在SQL Server 2008中運行具有以下條件的查詢。

Where FK.DT = CAST(DATEADD(m, DATEDIFF(m, 0, getdate()), 0) as DATE)  

查詢需要永遠以上述條件運行,但如果只是說

Where FK.DT = '2013-05-01' 

它在2分鍾內運行良好。 FK.DT鍵僅包含月份的起始數據的值。

任何幫助,我只是無知為什么會發生這種情況。

這可能更好:

Where FK.DT = cast(getdate() + 1 - datepart(day, getdate()) as date)

除非您使用跟蹤標志4199運行,否則會有一個影響基數估計的錯誤 在撰寫本文時

SELECT DATEADD(m, DATEDIFF(m, getdate(), 0), 0), 
       DATEADD(m, DATEDIFF(m, 0, getdate()), 0)

返回

+-------------------------+-------------------------+
| 1786-06-01 00:00:00.000 | 2013-08-01 00:00:00.000 |
+-------------------------+-------------------------+

問題在於,在推導基數估計時,問題中的謂詞使用第一個日期而不是第二個日期。 所以對於以下設置。

CREATE TABLE FK
(
ID INT IDENTITY PRIMARY KEY,
DT DATE,
Filler CHAR(1000) NULL,
UNIQUE (DT,ID)
)

INSERT INTO FK (DT)
SELECT TOP (1000000) DATEADD(m, DATEDIFF(m, getdate(), 0), 0)
FROM master..spt_values o1, master..spt_values o2
UNION ALL
SELECT               DATEADD(m, DATEDIFF(m, 0, getdate()), 0)

查詢1

SELECT COUNT(Filler)
FROM FK
WHERE FK.DT = CAST(DATEADD(m, DATEDIFF(m, 0, getdate()), 0) AS DATE)  

計划1

估計匹配行的數量將為100,000。 這是與日期'1786-06-01'匹配的數字。

但是以下兩個查詢

SELECT COUNT(Filler)
FROM FK
WHERE FK.DT = CAST(GETDATE() + 1 - DATEPART(DAY, GETDATE()) AS DATE)

SELECT COUNT(Filler)
FROM FK
WHERE FK.DT = CAST(DATEADD(m, DATEDIFF(m, 0, getdate()), 0) AS DATE)  
OPTION (QUERYTRACEON 4199)

給這個計划

計划2

由於更准確的基數估計,該計划現在只進行單個索引搜索而不是完整掃描。

在大多數情況下,以下可能適用。 在這種特定情況下,這是一個涉及DATEDIFF的優化器錯誤。 細節在這里這里 很抱歉懷疑t-clausen.dk,但他的答案根本不是一個直觀而合理的解決方案而不知道錯誤的存在。

所以假設DT實際上是DATE而不是像VARCHAR那樣愚蠢或者 - 更糟糕的是 - NVARCHAR - 這可能是因為你有一個緩存的計划在第一次執行時使用了一個非常不同的日期值,因此選擇了一個適應非常不同的典型數據的計划分配。 你有辦法克服這個問題:

  1. 通過添加OPTION (RECOMPILE)強制重新編譯計划。 您可能只需要執行一次,但是您獲得的計划可能不適合其他參數。 將選項一直留在那里的缺點是,每次查詢運行時都要支付編譯成本。 在很多情況下,這並不重要,我經常會選擇支付已知的小成本,而不是有時候查詢運行速度稍快,而其他時候運行速度非常慢。

     ... WHERE FK.DT = CAST(... AS DATE) OPTION (RECOMPILE); 
  2. 首先使用一個變量(這里不需要明確的CONVERT DATE ,請使用MONTH而不是像m那樣的簡寫 - 如果你沒記住所有縮寫的內容,習慣會導致真正有趣的行為,例如我打賭yw不會產生你期望的結果:

     DECLARE @dt DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0); ... WHERE FK.DT = @dt; 

    然而,在這種情況下可能會發生同樣的事情 - 參數嗅探可能會強制將次優計划用於表示不同數據偏斜的不同參數。

  3. 您還可以嘗試使用OPTION (OPTIMIZE FOR (@dt = '2013-08-01')) ,這會強制SQL Server考慮此值而不是用於編譯緩存計划的值,但這需要硬編碼的字符串文字,僅在8月的剩余時間內幫助您,此時您需要更新該值。 您還可以考慮OPTION (OPTIMIZE FOR UNKNOWN)

暫無
暫無

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

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