我创建了一个约束,可以防止一个表中的分配超出另一个表中的库存(请参阅我之前的问题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
答案 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的问题评论中,我在论坛帖子的帮助下得到了所有这些信息。