2016-08-31 73 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