AOI上传漏数据 SQL server

select * from xx (nolock) where testtime>'2026-08-08 08:10:10.321' and testtime<'2026-08-08 10:10:10.999' 在 2026-08-08 10:10:10.999 查询处理 50 行数据,但是同样的 sql 在 2026-08-08 10:12:10.999 查询有 51 行。 什么情况?

SQL Server 时间范围查询,过 2 分钟多出来 1 条数据

SQL:

sql

select * from xx with(nolock) 
where testtime>'2026-08-08 08:10:10.321' 
  and testtime<'2026-08-08 10:10:10.999'
  • 10:10:10.999 执行:返回 50 行
  • 10:12:10.999(2 分钟后)执行:返回 51 行

核心现象:查询条件完全没变,只是现实时间往后走了 2 分钟,结果行数 + 1。数据库 SQL Server。

根因 1:datetime 精度坑(最高概率)

datetime 类型精度是 3.33 毫秒,不是 1 毫秒。

它只能取:.000、.003、.007、.010 ……

999 毫秒给 datetime,内部会自动舍入到 10:10:11.000

plaintext

'2026-08-08 10:10:10.999'  → datetime 存储会变成 → 2026-08-08 10:10:11.000

你的条件:testtime < '2026-08-08 10:10:10.999'

等价于:testtime < 2026-08-08 10:10:11.000

那为什么同一套 SQL,不同现实时间查,行数不一样?

👉 那条多出的记录:testtime = '2026-08-08 10:10:10.997'(真实数据库存储时间)

  1. 10:10:10.999 现实时刻执行查询时:这条记录还没有插入表,所以返回 50
  2. 现实时间走到 10:12:10.999,这条数据已经被业务插入完成,条件满足,多 1 行,返回 51

SQL 文本条件写死,SQL 语句本身没有变,是表里面多插入了一条符合条件的新数据

你误以为查询快照是 “查询瞬间的数据库”,但确实就是:10:10 点查询的时候该行还没产生,2 分钟后数据落库了,再跑同样 where,自然多一行。


根因 2:WITH (NOLOCK) 脏读(次高概率)

nolock = 读未提交隔离级别,会读到未提交事务的数据。

场景还原:

  1. 10:10:10.999 你第一次执行 SQL: 另外会话开始 insert xx testtime='2026-08-08 10:10:10.997'事务还没 commit
    • nolock:未提交的数据,有可能看不见(取决于页锁状态)→ 返回 50 行。
  2. 10:12:10.999 第二次执行 SQL: 那个 insert 事务已经 commit,数据正式可见 → 返回 51 行。

⚠️ nolock 不保证每次返回完全一致快照,脏读、幻读是它的固有特性,不能拿来做报表统计、数据对账

重点区分:不是 SQL 变了,是数据库状态发生变化:新增数据 / 事务提交

根因 3:幻读(普通隔离级别也会)

默认 READ COMMITTED,也会发生幻读:两次相同查询,中间别的会话 insert 符合 where 条件的行,第二次查询行数增加。这是标准 SQL 幻读现象。


验证方法,快速定位

1、确认 testtime 字段数据类型

sql

sp_help 'xx'
  • 如果是 datetime → 毫秒舍入坑实锤。
  • 如果是 datetime2(3) → 没有 3.33ms 舍入问题。

2、把那条多出的行捞出来

直接把上限开大,看那一条的真实 testtime:

sql

select testtime,* 
from xx with(nolock)
where testtime>'2026-08-08 08:10:10.321' 
and testtime < '2026-08-08 10:10:11.000'
order by testtime desc

看最大的那一条的testtime真实值。

3、确认这条记录的生成时间

看是否就是在 10:10 ~10:12 之间插入的。

修复写法(时间范围标准最佳实践)

❌ 错误写法(datetime 极易踩坑)

sql

testtime < '2026-08-08 10:10:10.999'

✅ 推荐写法(左闭右开,通用,datetime/datetime2 全部兼容)

sql

WHERE testtime > '2026-08-08 08:10:10.321'
  AND testtime < '2026-08-08 10:10:11.000'

想要截止到 10:10:10 这一秒全部数据,上限直接写下一秒零点,不要写 999 毫秒。

关于 nolock 的风险提醒

做统计对账、导出业务数据,强烈不建议使用with(nolock)

  1. 脏读:读到回滚的垃圾数据
  2. 幻读:相同 SQL,不同时刻行数不一致(你现在遇到的)
  3. 有可能读重复行、漏行(分配页拆分,allocation scan)

如果要快照一致性,建议:

  • 开启数据库 READ_COMMITTED_SNAPSHOT ON,去掉 nolock,使用行版本快照读取。

复盘你的现象总结

你的 SQL 语句where 条件字符串完全没变,但是两次执行,底层表的数据发生变化:

第一次查询时,那条 51 行的数据还不存在(未插入 / 未提交);

过两分钟数据 insert+commit 完成,再次执行相同 where 条件,自然多返回一行。

nolock放大了这个现象,datetime毫秒舍入容易干扰你判断边界。



总之:sql server优化和使用门槛有点高。日志文件大,事务提交,电脑资源吃完等。

https://www.bilibili.com/opus/1186900004562796560?spm_id_from=333.1387.0.0

优化:

导出TXT(csv)数据有时候,还是靠谱。 www.erwa.cn 二娃备忘

实时数据导出 lastdata.txt 实时覆盖,只保留一条数据或者一个产品或者一次测试的数据。
历史数据导出 每个产品,每次测试导出一个文件。

导出到access,sqlite容易占用等。放弃


阅读剩余
THE END