時間欄位 (DATETIME/DATETIME2) 查詢是資料庫開發高頻需求。本文系統整理區間查詢、精確點查、模糊比對 (年/月/日/時/分) 寫法,並解析 CONVERT 導致索引失效的效能陷阱與最佳替代方案。
Table: dbo.tbBas_Subject
Column: CreateDate (DATETIME)
Sample: 2006-10-27 15:46:02.937
-- 最佳寫法:利用索引 Seek
SELECT TOP 4 *
FROM dbo.tbBas_Subject
WHERE CreateDate >= '2006-10-27 15:46:02.937'
AND CreateDate < '2006-10-27 16:17:15.377';
避免 BETWEEN 於毫秒精度資料,邊界包含問題易致漏/重。
SELECT *
FROM dbo.tbBas_Subject
WHERE CreateDate = '2006-10-27 16:17:15.377';
需完全匹配至毫秒/百納秒,實務上較少用。
| 需求 | 常見寫法 (❌ 索引失效) | 優化寫法 (✅ 索引可用) |
|---|---|---|
| 某年某月 | WHERE CONVERT(VARCHAR(10), CreateDate, 120) LIKE '2006-10-%' | WHERE CreateDate >= '2006-10-01' AND CreateDate < '2006-11-01' |
| 某日某時 | WHERE CONVERT(VARCHAR(23), CreateDate, 120) LIKE '2006-10-27 15:%' | WHERE CreateDate >= '2006-10-27 15:00:00' AND CreateDate < '2006-10-27 16:00:00' |
| 某日某分 | WHERE CONVERT(VARCHAR(23), CreateDate, 120) LIKE '2006-10-27 %46:%' | WHERE CreateDate >= '2006-10-27 15:46:00' AND CreateDate < '2006-10-27 15:47:00' |
CONVERT 是 **非確定性標量函數**,導致查詢優化器無法使用索引 Seek,只能掃描全表 (Scan)ALTER TABLE ... ADD CreateDate_YM AS CONVERT(CHAR(7), CreateDate, 120) PERSISTED; CREATE INDEX ... ON CreateDate_YM;-- 本月第一天 00:00:00
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
-- 本月最後一天 23:59:59.997
DATEADD(MILLISECOND, -3, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0))
拒絕 CONVERT ... LIKE 反模式,擁抱範圍查詢與 SARGable 寫法,讓時間查詢跑得又快又穩。