一次神奇的SQL 錯誤調試經歷

上週接到一個奇怪的bug,一個曾經運行得很好的存儲過程突然產生了錯誤的結果。

負責維護的兄弟們很負責任的對錯誤進行了跟蹤,並把錯誤定位一個如下的語句:

 

SELECT *

into SomeTable

FROM A join B on A.id=B.id

         join C on A.id=C.id

 

他們發現從SomeTable做查詢的時候,出來的結果比實際結果要大若干倍,挺齊整的,4倍或者2倍。

我的第一個感覺是JOIN出問題了,必然是形成了多對多的JOIN,比如A裏面有兩行ID一樣,B也有兩行ID一樣,結果表就會出現4行一樣的,得到一個4倍的結果,同樣的道理,如果A裏有一行,B裏有2行,結果就是2倍了。

 

於是我讓他們把SomeTable做成一個文本文件傳給我,嗯,300M的一個文件,BCP到數據庫裏,SELECT了一把,把出問題的那個ID抓出來,果然看到幾個重複的行。確切的說看到幾列幾乎一樣的行。

 

於是我就興沖沖的給他們說,檢察一下B和C上面的ID是否是unique的。

 

結果很快返回,說,是unique的。

 

這下我就傻眼了,這怎麼可能呢。重新仔細的看了便上次查處的結果,發現原本以爲重複的行,竟然不是完全相同的,嗯,有一列是不同的,而這一列正式B.ID。請注意,這些行在A.id上是一樣的。

 

這是一個驚人的發現,我通過A JOIN B JOIN C出來的結果居然不滿足JION的條件,這徹底顛覆了多年學習的關係數據庫知識。

 

毫無疑問,這是SQL Server的一個bug。然而如何建一個最小的數據集合來重現這個bug成了新的挑戰。因爲ABC三個表加起來有200G,而如果我刪掉一些看起來不相關的行,錯誤就消失了。我也試圖將ABC的JION分成兩步來做,

比如:

select *

FROM

(SELECT * from A JOIN B) AS D

   JOIN C ON...

結果仍然錯誤。

但是如果再進一步,

SElect * into temp from A join B...

Select * FROM temp join C...

錯誤消失了。

 

黔驢技窮之後,俺只好求救於SQl server的技術支持。剛開始發了封信,問有沒有人遇到過這種問題,ABC 交出的結果包含不滿足JOIN條件的行。結果一個小子居然說,這個問題太基本,這種情況根本不可能出現。 Sigh,大叔我也不是新手,簡單問題還需要勞煩您老人家嗎。

 

於是再仔細的描述問題,終於出了一個比較靠譜的哥們,讓我把執行計劃發給他看看。再然後另外一個哥們說可能是併發引起的問題,建議我用MAXDOP來限制參與執行的CPU個數。於是,我在原來的SQL後面加了個OPTION MAXDOP=1,果然經過7分鐘的執行,我得到了正確的結果。

 

然後給Sql server開bug,居然已經有hotfix了,估計我們不是第一個踩到雷的人。呵呵。

 

這個故事告訴我們,看問題要仔細,不要被自己的知識所矇蔽,然後描述問題要清楚,要找正確的人問問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章