萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> 資料庫綜合

SQL Server 日期時間範圍查詢最佳寫法:避開 DATEDIFF 陷阱、善用 SARGable 模式

日期範圍查詢是業務系統最高頻 SQL。寫法不同,效能差异可達 **10-100 倍**。本文對比 DATEDIFF、直接比較、BETWEEN、DATEADD 等寫法,揭示 **SARGable (Search ARGument Able)** 原則,給出最佳實踐模板。

效能對比實測 (50 萬筆測試資料)

寫法語句耗時執行計畫評價
DATEDIFF 函數WHERE DATEDIFF(s, GETDATE(), thetime) > 0~1200 ms**Clustered Index Scan** (全表掃描)❌ 最差:函數包裹欄位導致索引失效
直接比較 (推薦)WHERE thetime > GETDATE()~750 ms**Index Seek** (叢集/非叢集索引)✅ 最佳:SARGable,優化器可用索引尋找
BETWEEN 固定範圍WHERE datecol BETWEEN '2005-03-29' AND '2005-04-10'~750 msIndex Seek✅ 優:含邊界值,注意 datetime 精度陷阱
相對時間窗 (DATEADD)WHERE datecol >= DATEADD(day, -3, GETDATE())~750 msIndex Seek✅ 優:動態視窗,SARGable
DATEDIFF 近 N 天WHERE DATEDIFF(d, datecol, GETDATE()) < 3~1200 msClustered Index Scan❌ 差:同樣破壞 SARGable

核心原則:SARGable (Search ARGument Able)

**查詢條件必須讓優化器能夠使用索引尋找**。

  • ✅ Column > Value, Column >= Value, Column BETWEEN Value1 AND Value2, Column IN (...), Column LIKE 'prefix%'
  • ❌ FUNCTION(Column) > Value, Column + 1 > Value, COLUMN = UPPER('abc'), YEAR(Column) = 2024

鐵律:**永遠不要在 WHERE/JOIN/ON 條件中對欄位套用函數/運算**。將運算移至常數側:
WHERE OrderDate >= DATEADD(day, -7, GETDATE()) 而非 WHERE DATEDIFF(day, OrderDate, GETDATE()) <= 7。

常用日期範圍查詢最佳模板

-- 1. 固定日期範圍 (含邊界)
WHERE OrderDate >= '2024-01-01' AND OrderDate < '2024-02-01'  -- 推薦:半開區間 [start, end)
-- 或
WHERE OrderDate BETWEEN '2024-01-01' AND '2024-01-31 23:59:59.997'  -- 注意 datetime 精度 3.33ms

-- 2. 相對時間:過去 N 天/月/年
WHERE OrderDate >= DATEADD(day, -30, GETDATE())           -- 近 30 天
WHERE OrderDate >= DATEADD(month, -3, GETDATE())          -- 近 3 個月
WHERE OrderDate >= DATEADD(year, -1, GETDATE())           -- 近 1 年

-- 3. 本月 / 本週 / 今天
WHERE OrderDate >= DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)  -- 本月 1 號 00:00
WHERE OrderDate >= DATEADD(week, DATEDIFF(week, 0, GETDATE()), 0)    -- 本週一 00:00
WHERE OrderDate >= CAST(GETDATE() AS DATE)                             -- 今天 00:00 (SQL 2008+)

-- 4. 年初至今 (YTD)
WHERE OrderDate >= DATEADD(year, DATEDIFF(year, 0, GETDATE()), 0)

datetime vs datetime2 精度陷阱

  • datetime 精度 **3.33 毫秒**,最大值 23:59:59.997。
  • BETWEEN '2024-01-01' AND '2024-01-31 23:59:59.999' 會**漏掉 23:59:59.998/.999**。
  • **解法**:改用半開區間 WHERE dt >= '2024-01-01' AND dt < '2024-02-01',或改用 datetime2(3) 精度 1ms。

索引設計建議

  1. 日期欄位**建立非叢集索引** (單欄或包含在複合索引前段)。
  2. 大表考慮**分區表** (Partition Table) 按月/季分區,配合分區消除。
  3. 避免在索引欄位上隱含轉換:WHERE CAST(OrderDate AS DATE) = '2024-01-15' → 破壞 SARGable。
    改為:WHERE OrderDate >= '2024-01-15' AND OrderDate < '2024-01-16'。

進階:動態參數化查詢 (Stored Procedure)

CREATE PROC usp_GetOrdersByDateRange
    @StartDate DATETIME2 = NULL,
    @EndDate   DATETIME2 = NULL
AS
BEGIN
    SET NOCOUNT ON;
    SELECT OrderId, CustomerId, OrderDate, TotalAmount
    FROM Orders
    WHERE (@StartDate IS NULL OR OrderDate >= @StartDate)
      AND (@EndDate   IS NULL OR OrderDate <  DATEADD(day, 1, @EndDate))  -- 含結束日
    ORDER BY OrderDate DESC;
END;

遵循 SARGable 原則,日期查詢效能穩定可預測,是 SQL 開發與調優的基本功。

copyright © 萬盛學電腦網 all rights reserved