開發反饋,有一個SQL Server數據同步的做業,從Table1 拉取數據,主鍵是ID, 每次拉取批次數據的SQL語句是 select top (15) * from Table1(NOLOCK) where ID > ?,?表明的是上次同步批次中最後一個ID號。
某一次拉取到的數據爲 ID: 8101102121,8101103081 兩條數據。查表發現,這兩條記錄中間,還有一條記錄,即ID=8101102855,這條記錄沒有被拉取到。十分困惑,爲何會少拉一條記錄,是否拉取的時候,該條記錄沒有被建立?數據庫
Createtime | ID | Column3 | Column4 |
---|---|---|---|
2019-04-11 14:17:14.843 | 8101102121 | 已處理 | |
2019-04-11 14:17:17.190 | 8101102855 | 已處理 | |
2019-04-11 14:17:20.237 | 8101103081 | 已處理 |
剛開始看這個問題,也以爲很是奇怪。這個查詢語句中規中矩,從應用日誌來看,兩個ID之間的8101102855 確實是沒有被拉取到。
爲進一步定位問題,咱們分析具體的查詢語句。因爲咱們的服務器開啓了XEvent Trace,咱們定位到,當時在數據庫服務器端真正執行的語句以下,比開發反饋的更多一些條件:
服務器
select top 15 id from table1 with (nolock) where id > 8101101700 and Column4 not like '%(特殊需求)%' and createtime > '2018-07-19 00:00:00.000' order by id asc
咱們用這個查詢,在當前時間(2019-04-12 17:23:00)數據庫上進行查詢,確實能返回三條記錄。調試
經過數據庫明細記錄, 該語句在數據庫上的執行時間是:2019-04-11 14:17:21。對於ID=8101102855的記錄,其插入的時間是2019-04-11 14:17:17.190,比咱們的查詢時間早4秒,按道理應該是能查出來的。另外,在2019-04-11 14:17:21的時候,ID=8101102855 的記錄正在被更新。難道是咱們的查詢帶了NOLOCK,因此當前正在被更新的記錄跳過了?日誌
根據咱們的理解,NOLOCK至關於Read uncommitted, 是會讀取其餘事務「修改後未提交的「數據。也就是說,是可以讀出主鍵ID的。code
爲了可以更好的分析問題,咱們定點還原數據庫,還原到2019-04-11 14:17:20的時候,也就是比應用的查詢時間早1秒鐘。其數據記錄以下。在這個點上,ID=8101103081 尚未插入進來,但ID=8101102855,也就是咱們關注的ID,數據已經進來了。事務
Createtime | ID | Column3 | Column4 |
---|---|---|---|
2019-04-11 14:17:14.843 | 8101102121 | 已處理 | |
2019-04-11 14:17:17.190 | 8101102855 |
針對上面的數據,咱們執行查詢:
開發
select top 15 id from table1 with (nolock) where id > 8101101700 and Column4 not like '%(特殊需求)%' and createtime > '2018-07-19 00:00:00.000' order by id asc
奇怪的發現,這個查詢只返回ID=8101102121,ID=8101102855沒有返回。通過簡單的調試,咱們很快發現,是因爲這個查詢條件所致:字符串
Column4 not like '%(特殊需求)%'
ID=8101102855記錄在剛插入的時候,其Column4的值爲NULL,從語義上來說,確實是不包含"(特殊需求)"這個字符串的。但在SQL處理上,卻和咱們理解的不同。同步
SQL除了IS NULL和NOT NULL之外,只要出現NULL,值結果爲FALSE。簡單來講,對於查詢SELECT * from table where name != ‘test’,只要name值是NULL,不管用name=’test’仍是name != 'test', 都不能返回這一行。若是要返回的話,須要加IS NULL判斷:it
SELECT * from table where name != ‘test’ or name IS NULL
至此,問題真相大白,解決方案也就很簡單:調整查詢語句,加一個IS NULL判斷便可。
select top 15 id from table1 with (nolock) where id > 8101101700 and (Column4 not like '%(特殊需求)%' or Column4 IS NULL) and createtime > '2018-07-19 00:00:00.000' order by id asc
數據庫在碰到NULL的處理時候,要當心,查詢的判斷條件並非很明顯,要注意IS NULL的狀況。