检查约束调用函数在更新时不起作用

时间:2018-06-07 15:42:40

标签: sql-server constraints

我创建了一个约束,可以防止一个表中的分配超出另一个表中的库存(请参阅我之前的问题Here中的详细信息)。 但由于某种原因,只有当我插入新的分配时,约束才能按预期工作,但是在更新时它不会阻止违规。

这是我的约束:

([dbo].[fn_AllocationIsValid]([Itemid]) = 1)

这是功能:

CREATE FUNCTION [dbo].[fn_AllocationIsValid] (@itemId as int)  
RETURNS int  AS  
BEGIN 
DECLARE @isValid bit;

SELECT @isValid = CASE WHEN ISNULL(SUM(Allocation), 0) <= MAX(Inventory) THEN 1 ELSE 0 END
FROM Allocations A 
JOIN Items I ON I.Id = A.ItemId
WHERE I.Id = @itemId
GROUP BY I.Id;

RETURN @isValid;
END

感谢。

增加:

以下是我的表格:

CREATE TABLE [allocations] (
[userID] [bigint] NOT NULL ,
[itemID] [int] NOT NULL ,
[allocation] [bigint] NOT NULL ,
CONSTRAINT [PK_allocations] PRIMARY KEY  CLUSTERED 
(
    [userID],
    [itemID]
)  ON [PRIMARY] ,
CONSTRAINT [FK_allocations_items] FOREIGN KEY 
(
    [itemID]
) REFERENCES [items] (
    [id]
) ON DELETE CASCADE  ON UPDATE CASCADE ,
CONSTRAINT [CK_allocations] CHECK ([dbo].[fn_AllocationIsValid]([Itemid], [Allocation]) = 1)
) ON [PRIMARY]

CREATE TABLE [dbo].[Items](
[Id] [int] NOT NULL,
[Inventory] [int] NOT NULL
) ON [PRIMARY]
GO

INSERT INTO Items (Id, Inventory) VALUES (2692, 336)

INSERT INTO Allocations (UserId, ItemId, Allocation) VALUES(4340, 2692, 336)
INSERT INTO Allocations (UserId, ItemId, Allocation) VALUES(5895, 2692, 0)

以下语句执行失败,但不会:

update allocations set allocation = 5
where userid = 5895 and itemid = 2692

1 个答案:

答案 0 :(得分:7)

好吧,我刚刚学到了一些东西。

事实证明,带有CHECK CONSTRAINTS和UPDATES,只有在CONSTRAINT中引用的其中一列发生变化时才会检查CONSTRAINT。

在您的情况下,您的CONSTRAINT正在检查您传递ItemID的UDF。

在您的更新中,可能您只是更改了Allocation而不是ItemID的值,因此优化程序会认为&#34;如果ItemID没有更改,然后没有必要检查约束&#34;,它没有,并且UPDATE成功,即使CONSTRAINT应该失败它。

我通过重建你的函数和约束并向其添加Allocation来测试它:

ALTER FUNCTION [dbo].[fn_AllocationIsValid] (@itemId as int, @Allocation int)  
RETURNS int  AS  
BEGIN 
DECLARE @isValid bit;

SELECT @isValid = CASE WHEN ISNULL(SUM(Allocation), 0) <= MAX(Inventory) THEN 1 ELSE 0 END
FROM Allocations A 
JOIN Items I ON I.Id = A.ItemId
WHERE I.Id = @itemId
GROUP BY I.Id;

RETURN @isValid;
END

ALTER TABLE [dbo].[Allocations]  WITH CHECK ADD  CONSTRAINT [CK_Allocations] 
CHECK  (([dbo].[fn_AllocationIsValid]([Itemid], Allocation)=(1)))
GO

请注意,我必须首先删除原始约束并截断/重新填充表格,但这并不需要我向您展示如何操作。

另请注意,Allocation并未涉及该功能的任何逻辑。我根本没有改变逻辑,只是添加了@Allocation的参数。该参数永远不会被使用。

然后,当我执行UPDATE将Allocation的SUM提高到MAX以上时,我得到了预期的错误:

  

UPDATE语句与CHECK约束冲突   &#34; CK_Allocations&#34 ;.冲突发生在数据库&#34; Tab_Test&#34;,表中   &#34; dbo.Allocations&#34;

为什么呢?因为即使@Allocation在函数逻辑中没有使用,CONSTRAINT中的Allocation引用,所以当Allocation的值发生变化时,优化器会检查约束。

有些人认为,由于这样的事情,使用TRIGGER而不是调用UDF的CHECK CONSTRAINT总是更好。我不相信,我还没有看到任何可重复的实验来证明这一点。但是我想把你想要的方式留给你。

希望这些信息对未来的读者有用。

PS:在归功于this blog on the subject的问题评论中,我在论坛帖子的帮助下得到了所有这些信息。

相关问题