651 字
3 分钟
SQL Server 日期查询实战:从基础到高级优化技巧

引言#

在数据库开发中,日期筛选是最高频的操作之一。虽然 DATEDIFFDATEADD 能解决 90% 的问题,但在处理大数据量时,错误的写法会导致查询效率直线下降。本文不仅分享常用的查询技巧,还将教你如何编写“索引友好”的日期 SQL。


一、 常用时间段筛选(常规写法)#

这些语句非常适合报表统计或小规模数据查询。

1. 今天、昨天与过去 N 天#

  • 今天WHERE DATEDIFF(dd, [DateColumn], GETDATE()) = 0
  • 昨天WHERE DATEDIFF(dd, [DateColumn], GETDATE()) = 1
  • 过去 7 天内WHERE DATEDIFF(dd, [DateColumn], GETDATE()) <= 7

2. 本月与本年#

  • 本月WHERE DATEDIFF(mm, [DateColumn], GETDATE()) = 0
  • 本年WHERE DATEDIFF(yy, [DateColumn], GETDATE()) = 0

二、 进阶日期计算(精准控制)#

3. 获取边界日期(月初、月末)#

在旧版本中,我们常用 DATEADD 组合计算,但在 SQL Server 2012 之后,有更优雅的选择:

  • 本月第一天
SELECT DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0);
  • 本月最后一天(新推荐)
SELECT EOMONTH(GETDATE()); -- SQL Server 2012+ 支持
  • 本月有多少天
SELECT DAY(EOMONTH(GETDATE()));

4. 计算周一(不受特定语言设置影响)#

直接使用 DATEDIFF 计算基准日期(0 代表 1900-01-01,那是周一)的差值:

SELECT DATEADD(wk, DATEDIFF(wk, 0, GETDATE()), 0) AS ThisMonday;

三、 性能优化:编写索引友好的查询 (SARG)#

这是本文最重要的部分。 > 警惕:当你写 WHERE DATEDIFF(dd, CreateTime, GETDATE()) = 0 时,SQL Server 必须对表中每一行执行函数计算,这会导致索引失效(Index Scan)。

优化方案:计算范围,而不是计算字段。

如果你想查询今天的数据,推荐写法:

-- 这种写法支持索引寻址 (Index Seek)
DECLARE @Today DATE = GETDATE();
SELECT * FROM 表名
WHERE CreateTime >= CAST(@Today AS DATETIME)
AND CreateTime < CAST(DATEADD(dd, 1, @Today) AS DATETIME);

四、 常用函数速查表#

函数说明示例
GETDATE()获取当前系统时间2024-01-14 14:30:00
DATEPART(dw, dt)返回周几(数字)1 (周日) 或 2 (周一)
DATENAME(month, dt)返回月份名称January一月
CONVERT(varchar, dt, 23)格式化日期2024-01-14 (ISO 格式)

五、 常见问题 FAQ#

  • Q: 如何计算两个日期间隔的分钟数?

  • SELECT DATEDIFF(mi, '2024-01-14 10:00', GETDATE());

  • Q: 为什么我的周一计算不对?

  • SQL Server 的 DATEFIRST 设置可能不同。建议使用本文“月初”类似的基准日期计算法,它不受系统语言设置(@@DATEFIRST)的影响。


总结#

掌握日期函数只是第一步,意识到函数对索引的影响才是进阶资深开发者的关键。在处理百万级以上的数据表时,请务必使用 Range Query(范围查询)代替字段上的 DATEDIFF

SQL Server 日期查询实战:从基础到高级优化技巧
https://sw.rscclub.website/posts/sqlcxjq/
作者
杨月昌
发布于
2018-11-18
许可协议
CC BY-NC-SA 4.0