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 ʕ•͡ᴥ•ʔ