sql server中用getdate()做步骤计时的主要坑在于精度不足(仅3.33ms)和并发下时间戳混淆;应改用sysdatetime()或getutcdate(),配合唯一上下文标识、独立起止时间记录及datetime2高精度字段。

SQL Server里用GETDATE()做步骤计时的坑在哪
直接用 GETDATE() 相减看似简单,但容易忽略精度和并发干扰:它返回 datetime 类型(精度仅3.33ms),在快速执行的步骤间可能读到相同值;若存储过程被并发调用,日志表没加事务或唯一标识,时间戳和步骤就对不上。
实操建议:
- 改用
SYSDATETIME()或GETUTCDATE()(精度100ns),尤其适合毫秒级耗时判断 - 每条日志必须带唯一上下文标识,比如传入的
@BatchId或NEWID()生成的会话ID - 避免在单条
INSERT中多次调用GETDATE()——函数会在语句开始时求值一次,不是“实时取”
怎么把各步骤时间差写进日志表(含字段设计)
日志表不能只存时间戳,否则无法还原执行流。推荐至少包含:LogId(自增)、BatchId(关联同一过程调用)、StepName(如 'LoadStaging')、StartTime、EndTime、DurationMs(计算列或插入时算好)。
关键写法示例:
DECLARE @t1 DATETIME2 = SYSDATETIME(); <p>-- 执行步骤A UPDATE dbo.Staging SET Status = 'Processed' WHERE BatchId = @BatchId;</p><p>INSERT INTO dbo.ProcLog (BatchId, StepName, StartTime, EndTime, DurationMs) SELECT @BatchId, 'UpdateStaging', @t1, SYSDATETIME(), DATEDIFF_BIG(MILLISECOND, @t1, SYSDATETIME());</p>
注意:DATEDIFF_BIG 是 SQL Server 2016+ 的安全选择,避免 DATEDIFF 在大跨度时溢出;老版本可用 DATEDIFF(MILLISECOND, ...),但需确认步骤间隔不会超24天。
为什么不能靠前后两条日志的EndTime/StartTime相减算耗时
这种做法常见但不可靠:如果步骤B因锁等待延迟启动,它的 StartTime 就不是步骤A结束的准确时刻;更糟的是,日志表若没主键或没及时刷盘,两条记录顺序还可能颠倒。
正确方式是每个步骤自己记起点+终点:
- 步骤A:声明
@startA→ 执行 → 插入日志(含@startA和当前时间) - 步骤B:声明
@startB→ 执行 → 插入日志(含@startB和当前时间) - 不依赖日志表中其他行的时间字段做计算
这样即使某步失败回滚,只要日志INSERT在事务外(或用 TRY...CATCH + XACT_ABORT OFF 单独提交),时间数据仍可追溯。
日志表太大怎么办?要不要加索引
高频过程每天几万条日志很常见。不加索引,按 BatchId 查执行流会变慢;但全表扫描写入又拖慢主流程。
折中方案:
- 给
BatchId建非聚集索引(INCLUDE(StepName, StartTime, DurationMs)),查单次执行最常用 - 日志表定期按月分区(SQL Server 2016+),或用
sp_executesql动态清理30天前数据 - 避免在日志INSERT里调用
GETDATE()多次——每次都是函数调用开销,累积起来影响主逻辑性能
真正容易被忽略的点是:时间字段类型必须统一为 DATETIME2(3) 或更高精度,否则 DATEDIFF 算出来的毫秒值其实是四舍五入后的,看不出真实瓶颈在哪。











