2016-08-31 74 views
2

我在存儲過程中有一個SQL Server更新語句,它在執行過程中違反了約束,但是約束在整個更新語句完成後有效。如何按特定順序執行SQL Server更新?

的說法是:

UPDATE SomeTable SET FieldA = 1 WHERE FieldB = @SomeFieldBValue; 

表中的行SomeTable形成子行引用父行的層次關係。該約束確保父行不能將FieldA設置爲非空值,除非所有子行都將FieldA設置爲非空值。

因此,舉例來說,讓我們假設SomeTable包含以下數據:

Id | ParentId | FieldA | FieldB 
-- | -------- | ------ | ------ 
1 | NULL  | NULL | 123 
2 | 1  | NULL | 123 

現在,當更新語句執行失敗約束髮生衝突,因爲第1行是不允許有FIELDA設爲非除非它的所有子元素都將FieldA設置爲非空值。

暫時禁用約束不是一個選項,因爲用戶沒有權限執行此操作。

+1

而更新語句不按某種順序處理。這是一套基於操作。你需要在這裏執行2個獨立的更新語句。 –

+0

謝謝,我編輯了這個問題來刪除排序的假設。 – flange

回答

1

這樣的觸發器會完成同樣的事情嗎?

create trigger ... 
if exists (
    select i.Id 
    from 
     inserted i left outer join inserted i2 
      on i2.ParentId = i.Id 
    group by i.Id 
    having not (count(i.FieldA) = 0 or count(i2.FieldA) = count(i2.Id)) 
) rollback; 
+0

我用了這個謝謝的版本。它實際上導致了一個更可讀的約束,因爲我能夠引用一個計算列。我明白使用觸發器會有危險,但在這種情況下,作爲觸發器來實現約束是最有意義的,並給出了一個簡單的解決方案。 – flange

-1

如果你不能禁用約束,恐怕你不能用一個語句來做到這一點。您必須創建一個循環並以「正確順序」手動執行更新。

僞代碼:

Begin Transaction 
Do 
    recordsAffected = UPDATE SomeTable 
         SET FieldA = 1 
         WHERE (FieldA IS NULL OR FieldA <> 1) 
         AND FieldB = @SomeFieldBValue 
         AND NOT EXISTS (child with FieldA set to null); 
While recordsAffected > 0 
Commit Transaction 

翻譯這對T-SQL或您所選擇的客戶端語言留作練習。 :-)

+0

@Downvoter:反饋表示讚賞。 – Heinzi

0

不要在SQL中使用WHILE循環,SQL是基於集合的語言而不是程序性的。只需將父代和所有孩子選入#TEMP表,更新臨時錶行,刪除原始代碼並插入新代碼:

BEGIN TRAN 

    SELECT Id, ParentID, FieldA, FieldB, 
    INTO #TEMP1 
    FROM SomeTable 
    WHERE FieldB = SomeValue 

    UPDATE #TEMP1 SET FieldA = 1 

    DELETE SomeTable where FieldB = SomeValue 

    INSERT SomeTable Set ..... FROM #TEMP1 
COMMIT 
+0

用INSERT SomeTable替換最後的DELETE FROM#TEMP1 –

+1

使用自動生成的主鍵進行混合? – flange