Sql-Server

未送出的讀取在 UPDATE WHERE NOT EXISTS 中不起作用

  • November 7, 2019

我無法強制 SQL Server (2016) 尊重在同一語句中更新的值。

我需要做的是使用映射表()更新舊值(下面展示程式碼中a的表中的列)到新值(表中的列) #t``#u``new_a``#u

更新後包含舊值的行將被刪除。

這是展示程式碼:

DROP TABLE IF EXISTS #t
DROP TABLE IF EXISTS #u

CREATE TABLE #t (a int, b int)

CREATE UNIQUE CLUSTERED INDEX t_idx ON #t
(
   a ASC,
   b ASC
)

CREATE TABLE #u (old_a int, new_a int)

INSERT INTO #t (a, b)
VALUES
(1,1),
(1,2),
(2,1),
(2,2)


INSERT INTO #u (old_a, new_a)
VALUES
(1, 3),
(2, 3)

UPDATE t
SET t.a = u.new_a
FROM #t AS t WITH (READUNCOMMITTED)
JOIN #u AS u
ON u.old_a = t.a

WHERE NOT EXISTS (
   SELECT 1
   FROM #t AS t_top WITH (READUNCOMMITTED)
   WHERE 1=1
   AND t_top.a = u.new_a
   AND t_top.b = t.b)

這導致

Cannot insert duplicate key row in object 'dbo.#t' with unique index 't_idx'. The duplicate key value is (3, 1).

雖然我可以通過其他方式(例如禁用唯一索引,然後刪除重複項),但我想知道為什麼 SQL Server 不想讀取新更新的值。

另外,是否有可能在每行之後以某種方式更新送出?最好不使用儲存過程。

SQL Server 版本是 2016

編輯: SQL Server 在檢查 FK 違規時看到這些值,但在 WHERE EXISTS 子句中沒有

編輯2: 我希望這會如何工作的圖片

這裡沒有“第一次更新”、“第二次更新”,有一個語句,一個原子更新。這update會產生 2 行相同的行。

如果您希望“第二個”update不成功,您應該update在 1 內寫 2 個顯式 s,而不是一個transaction。但是在這種情況下, nolock 沒有任何意義,因為它transaction看到自己的未送出值根本沒有任何意義hint

相反..我認為您的數據庫伺服器確實讀取了更新的值…

詳細說明:簡單地視覺化(如果需要在一張紙上),更新期間會發生什麼

before 
(1,1),(1,2),(2,1),(2,2)
1st update ->
(3,1),(1,2),(2,1),(2,2)
2nd update ->
(3,1),(3,2),(2,1),(2,2)
3rd update ->
(3,1),(3,2),(3 ***,1),(2,2)

如果我正確理解您的更新語句 - 在第一步之後 #t 的第一行將是 (3,1) - 然後在第三次執行中 #t 的第三個條目也將變為 (3,1) - 您的組合是唯一的索引不允許(在上面的行中由 *** 視覺化)

在跨表更新期間禁用約束並在之後再次啟用它們是您和類似情況的常見做法。

但是,如果我看到您的範例表 #u .. 您將無法 .. 因為您的表中有數據違反了您的約束.. 請檢查 #u

例子

(3,1),
(4,2)

應該管用

編輯:

WHERE NOT EXISTS (
   SELECT 1
   FROM #t AS t_top WITH (READUNCOMMITTED)
   WHERE 1=1
   AND t_top.a = u.new_a
   AND t_top.b = t.b)

應該給出評估為 true 的 WHERE 1 進行詳細說明,您選擇的 where 條件變為 false 使選擇傳遞 0 或 false(1=1 AND 2=3 AND 1=1) - 如果我輸入條目#3 的值的 #t - …這被 NOT EXISTS 否定,所以你的更新完成了……並且爆炸衝突的唯一索引

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