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

Dav*_*het 4 sql-server constraints

我创建了一个防止分配在一个表中超过另一个表库存(请参见我刚才的问题的细节约束这里).但由于某种原因,只有当我插入新的分配时,约束才能按预期工作,但是在更新时它不会阻止违规.

这是我的约束:

([dbo].[fn_AllocationIsValid]([Itemid]) = 1)
Run Code Online (Sandbox Code Playgroud)

这是功能:

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
Run Code Online (Sandbox Code Playgroud)

谢谢.

添加:

这是我的表格:

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)
Run Code Online (Sandbox Code Playgroud)

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

update allocations set allocation = 5
where userid = 5895 and itemid = 2692
Run Code Online (Sandbox Code Playgroud)

Tab*_*man 9

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

事实证明,使用CHECK CONSTRAINTS和UPDATES时,只有在CONSTRAINT中引用的一个列发生更改时才会检查CONSTRAINT.

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

在你的UPDATE中,大概你只是改变了值Allocation,而不是ItemID,所以优化器认为"如果ItemID没有改变,那么就没有必要检查约束",它没有,并且UPDATE成功,即使CONSTRAINT应该失败了.

我通过重建你的函数和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
Run Code Online (Sandbox Code Playgroud)

和:

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

请注意,我必须首先DROP原始约束并截断/重新填充表,但这不是你需要我告诉你如何做的.

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

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

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

为什么?因为即使@Allocation使用在该函数的逻辑,所述Allocation列被引用在约束,所以优化确实检查约束时的值Allocation的变化.

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

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

PS:归功于合适的功劳,我在论坛帖子中通过对该问题的评论的一些帮助了解了所有这些,这导致了关于这个主题的博客.

  • @TabAlleman 所以我原来的答案在设计上是正确的。:) 严肃地说,了解约束是一件非常好的事情。希望我能投不止一票 (2认同)