Sql-Server-2012

為什麼查詢違反外鍵的記錄會返回不正確的結果?

  • February 27, 2020

在添加新外鍵之前對錶進行一些預檢查時,我們正在查詢有多少目前行會違反新鍵。這是一個相當活躍的數據庫,在相關表上幾乎不斷地插入。

FK 將從 MessagePatientIdentifier.MessageID 到 Message.MessageID。

我們使用的查詢盡可能簡單:

select * from MessagePatientIdentifier as mpi
where
   MessageID not in (select MessageID from Message)

我們看到的問題是從 MessagePatientIdentifier 返回的行

MessagePatientID                     | MessageID 
553bde76-47d4-4ec3-96d1-b5d2e98931e1 | 7d45464d-8cc4-4a2e-8828-020722165b39

在這種情況下,當你 then 時select * from Message where MessageID = 7d45464d-8cc4-4a2e-8828-020722165b39,該記錄確實存在。

然後我們繼續嘗試相同的查詢,但在一個確實有 FK 的表上,以相同的方式引用 Message 表。相同的結果…查詢報告子表記錄存在而沒有相應的父表(消息)記錄。

MessageRecipID                       | MessageID 
26d6d632-87b3-407e-aeb0-04552981e5f8 | 750f0fb4-3e6c-485d-996e-f061f8caa360

然後,再次如果你select * from Message where MessageID = 750f0fb4-3e6c-485d-996e-f061f8caa360,這將返回記錄。

此數據通過來自 Mirth Server 的儲存過程以及通過 BizTalk WCF-SQL 發送埠傳入。proc 插入消息記錄,獲取 newuniqueidentifier作為輸出變數,然後使用它來呼叫輔助儲存 procs 以插入到MessagePatient.MessageIDandMessageRecip.MessageID中。

這是預期的行為,我只是沒有受過 SQL 的內部工作原理的教育嗎?從技術上講,我相信 BizTalk 在事務中執行所有內容,因此它不應該不同步,即使它做了,如果不是從消息插入,它會在哪裡獲得 MessageID 值?

我在這裡想念什麼?

這是一個相當活躍的數據庫 

如果您可以在某處恢復靜態備份,並對該不變的副本進行分析,我想您會發現您所看到的奇怪行為消失了。

讀已送出

SQL Server 中的預設隔離級別是READ COMMITTED。在此隔離級別下,您讀取目前送出到數據庫的數據。它提供的唯一保證是,當 SQL Server 讀取給定頁面時,它永遠不會返回未送出的結果(稱為臟讀)。

在您的情況下,您正在掃描兩個表並比較它們。在 SQL Server 進行掃描時,會發生數據移動。

  • 您讀取了前幾頁數據並獲得了第一位數據。
  • 您尚未閱讀的某些行已更新。也許 theMessageSubject或其他一些列被更新為更長的值,這會導致頁面拆分。或者,鍵列可能已更新,並且該行物理上將索引“向上”移動到您已經閱讀的部分。
  • 在這兩種情況下,一行(或多行)都可能從您尚未閱讀的地方移動到您已經閱讀的地方。您將永遠看不到該行,因為它四處移動。這會導致該行從您的掃描中“失去”。
  • 一行也有可能向另一個方向移動:從一個你已經閱讀過的地方到一個你還沒有到達過的地方。在這種情況下,您將看到該行兩次,並且您的結果將有一個神秘的雙重結果。

您的範例使用整數,但在您的問題中您提到uniqueidentifier. 由於uniqueidentifiers 是隨機的,隨機性意味著行不斷地插入到表上的隨機位置。這增加了頁面填滿並且必須拆分以容納新頁面的機會,並增加了您看到這些現象的機會。

閱讀上述連結文章中標題為“鎖定已送出行為”的部分以獲得詳盡的解釋。

修復?

如果要避免這些關閉現象,請不要使用 SQL Server 中的預設隔離級別。最喜歡的預設隔離級別是READ COMMITTED SNAPSHOT,它可以避免這些問題。(閱讀此處的所有隔離級別,以決定哪一個適合您。)

或者,如果只是為了一次性分析,您可以使用數據庫快照創建靜態圖像進行查詢,或者您可以停止寫入數據庫,或在其他地方恢復副本。停止寫入將停止數據移動,您不會遇到問題。

引用自:https://dba.stackexchange.com/questions/260664