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'(真实数据库存储时间)
- 10:10:10.999 现实时刻执行查询时:这条记录还没有插入表,所以返回 50
- 现实时间走到 10:12:10.999,这条数据已经被业务插入完成,条件满足,多 1 行,返回 51
SQL 文本条件写死,SQL 语句本身没有变,是表里面多插入了一条符合条件的新数据。
你误以为查询快照是 “查询瞬间的数据库”,但确实就是:10:10 点查询的时候该行还没产生,2 分钟后数据落库了,再跑同样 where,自然多一行。
根因 2:WITH (NOLOCK) 脏读(次高概率)
nolock = 读未提交隔离级别,会读到未提交事务的数据。
场景还原:
- 10:10:10.999 你第一次执行 SQL: 另外会话开始
insert xx testtime='2026-08-08 10:10:10.997',事务还没 commit。- nolock:未提交的数据,有可能看不见(取决于页锁状态)→ 返回 50 行。
- 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)
- 脏读:读到回滚的垃圾数据
- 幻读:相同 SQL,不同时刻行数不一致(你现在遇到的)
- 有可能读重复行、漏行(分配页拆分,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容易占用等。放弃