必须使用datetime2类型,因datetime会导致索引失效、精度丢失(3.33ms)和隐式转换;存储过程参数须声明为datetime2(3)或datetime2(7),字符串需显式try_convert,where条件禁止函数包裹日期列。

datetime2 是必须用的类型,datetime 参数会导致索引失效、精度丢失和隐式转换——这不是优化建议,是硬性要求。
存储过程参数必须声明为 datetime2
SQL Server 存储过程中传时间范围,@start_date 和 @end_date 不能用 datetime。它精度只有 3.33ms,范围窄(1753–9999),且与表中 datetime2(7) 字段比较时,SQL Server 会把整列转成 datetime 再比,索引直接失效。
- 统一用
@start_date datetime2(3)(毫秒级足够,节省空间)或@end_date datetime2(7)(与业务表字段完全对齐) - 如果接收字符串(如
@date_str NVARCHAR(20)),开头就显式转换:SET @start_date = TRY_CONVERT(datetime2(3), @date_str); IF @start_date IS NULL RETURN;,别依赖隐式行为 - 调用方传
GETDATE()或应用层生成的精确时间戳时,datetime2不会截断;datetime可能丢掉毫秒甚至报溢出错误
WHERE 条件里禁止函数包裹日期列
WHERE CONVERT(date, order_time) = '2024-06-09' 看着省事,但会让 SQL Server 放弃 order_time 上的索引,降级为全表扫描——哪怕表只有几万行,延迟也会明显升高。
- 查某一天全部数据:写成
order_time >= '2024-06-09' AND order_time (注意是 <code>,不是 <code>) - 查最近 7 天:用
order_time >= DATEADD(day, -7, GETDATE()),别用BETWEEN——它闭区间特性在含毫秒时容易漏掉23:59:59.9999999 - 若必须按日期聚合,先在 CTE 缩小数据集:
WITH filtered AS (SELECT * FROM orders WHERE order_time >= @s AND order_time
复合索引的日期字段必须放最左位
假设常查 “某用户在某时间段内的订单”,条件是 WHERE user_id = @uid AND order_time BETWEEN @s AND @e,但索引建成了 IX_user_time ON orders (user_id, order_time)——这等于白建。SQL Server 只能用 user_id 做等值查找,再对匹配到的所有行逐个检查 order_time。
- 必须把高频筛选的日期字段放在索引最左侧:
CREATE INDEX IX_order_time_user ON orders (order_time, user_id) - 如果还带状态过滤(如
status IN ('paid', 'shipped')),可扩展为(order_time, status, user_id),但别把低选择性字段(如status)放最前 - 执行计划里确认
order_time出现在Seek Predicates中,而不是Residual Predicate
MySQL / PostgreSQL 的时间范围写法差异要盯紧
不同数据库对边界处理、字符串解析、时区默认行为差异很大,不能套用同一套写法。
- MySQL 中
BETWEEN '2024-06-01' AND '2024-06-30'会被补成'2024-06-01 00:00:00'和'2024-06-30 00:00:00',当天数据几乎全丢;应改用fecha >= '2024-06-01' AND fecha - PostgreSQL 的
timestamp with time zone字段,'2024-06-01'默认按服务器时区解释,线上务必显式写'2024-06-01 UTC'::timestamptz - 跨库迁移或共用 ORM 时,统一用
YYYY-MM-DD HH:MM:SS格式最安全,避免06/01/2024这类依赖SET DATEFORMAT的写法
真正卡住性能的,往往不是逻辑复杂度,而是参数类型和索引顺序这两个看似基础的点。只要 datetime2 声明、开区间写法、日期字段前置这三点做实,90% 的时间范围查询慢问题就解了一半。











