[經驗分享] 你抓對了查詢的日期區間了嗎?

Getting bit by datetime rounding problem

分享一個最近幫同事處理資料移轉遇到的一個小問題

A君要在一個沒有 primary key 的 table 做資料移轉

phase 1 被告知先移轉一段日期的資料

之後 phase 2 才會有 down time 去轉最後一部份的資料

於是在模擬環境寫了兩段語法如下

INSERT INTO [TestDB].[dbo].[NewTable]
SELECT * FROM [TestDB].[dbo].[OldTable] WITH(NOLOCK) 
WHERE StartTime BETWEEN '2017-10-30 00:00:00.000' AND '2017-10-31 23:59:59.999' 
INSERT INTO [TestDB].[dbo].[NewTable]
SELECT * FROM [TestDB].[dbo].[OldTable] WITH(NOLOCK) 
WHERE StartTime >= '2017-11-01 00:00:00.000'

最後在驗證新舊資料筆數的時候發現怎麼兩邊的筆數不一致 ?

原來有個小細節被忽略了 所以在查詢日期區間時的進位問題導致重複寫入了相同資料

此狀況在於 SQL Server 對 datetime 的進位處理 我們實際來驗證一下好了

USE TestDB
GO

CREATE TABLE OldTable
(
	ID int,
	StartTime datetime
)
GO
INSERT INTO	TestDB.dbo.OldTable (ID, StartTime)
VALUES 
	(1, '2017-10-30 00:00:00.000'),
	(2, '2017-10-30 00:00:00.001'),
	(3, '2017-10-30 23:59:59.996'),
	(4, '2017-10-30 23:59:59.997'),
	(5, '2017-10-30 23:59:59.998'),
	(6, '2017-10-30 23:59:59.999'),
	(7, '2017-10-31 00:00:00.000'),
	(8, '2017-10-31 00:00:00.001'),
	(9, '2017-10-31 23:59:59.996'),
	(10, '2017-10-31 23:59:59.997'),
	(11, '2017-10-31 23:59:59.998'),
	(12, '2017-10-31 23:59:59.999'),
	(13, '2017-11-01 00:00:00.000'),
	(14, '2017-11-01 00:00:00.001'),
	(15, '2017-11-01 23:59:59.996'),
	(16, '2017-11-01 23:59:59.997'),
	(17, '2017-11-01 23:59:59.998'),
	(18, '2017-11-01 23:59:59.999')
GO

奇怪了 ID 2 的 '2017-10-30 00:00:00.001' 怎麼被變成 '2017-10-30 00:00:00.000' 了 ?

ID 9 與 11 怎麼都變成 '2017-10-31 23:59:59.999' 了 ?

ID 12 的 '2017-10-31 23:59:59.999' 怎麼變成 '2017-11-01 00:00:00.000' 了 ? 

ID 13 與 14 怎麼也被查出來了 ? 

這是什麼巫術 !! 完蛋了 SQL Server 中毒了嗎 !?

 

是不是 BETWEEN 搞的鬼 !? 換個查詢語法來看看

怎麼還是一樣的結果 !?

 

原來 SQL Server 中 datetime 精度只有 3.33 毫秒
(更精確的說 實際上是 1/300 秒)

在無法取得正確的 001... 996 998 999 Milliseconds 下 SQL Server 就會自動取捨進位

為了解決移轉資料筆數的一致性 所以選用以下的條件來切割批次資料

最後來看看以下的查詢會變成什麼有趣的樣子

一個不小心就要被雷了啊XD

have fun ʕ•͡ᴥ•ʔ