尽管函数 returns 正确,但更新时使用函数的检查约束失败

Check constraint with function fails on update although function returns correctly

我正在尝试在 table CheckTable 上实现 CHECK constraint 以确保只有一行具有相同 [=19= 的给定行数]col2 值可以将 ok 位设置为 1。

这是我要解决的问题,我正在努力理解如何在检查约束中使用该函数,因为第二次更新通过了检查,尽管该函数似乎可以正常工作

CREATE TABLE [dbo].[CheckTable] (col1 int, col2 int, ok bit)
GO

CREATE FUNCTION [dbo].[CheckIfOk] 
(
    @col2 int
)
RETURNS bit
AS
BEGIN
    DECLARE @OK bit;
    IF(EXISTS(SELECT * FROM [dbo].[CheckTable] WHERE col2 = @col2 AND ok = 1))
    SET @OK = 1
    ELSE
    SET @OK = 0
    RETURN @OK
END
GO

ALTER TABLE [dbo].[CheckTable]
ADD CONSTRAINT chk_col2_ok CHECK (dbo.CheckIfOk(col2) = 0)
GO

INSERT INTO [dbo].[CheckTable] (col1, col2, ok) VALUES (1, 1, 0),(2, 1, 0)
GO

SELECT [dbo].[CheckIfOk](1)
GO

UPDATE [dbo].[CheckTable] SET ok = 1 WHERE col1 = 1
GO

SELECT [dbo].[CheckIfOk](1)
GO

UPDATE [dbo].[CheckTable] SET ok = 1 WHERE col1 = 2
GO

这可能很明显,但我似乎无法理解。

只是不要 - 标量函数是臭名昭著的效率问题。您的目标可以更好地(IMO)通过更简单的方法实现 - 一个独特的过滤索引。

if object_id('dbo.CheckTable') is not null 
   drop table dbo.CheckTable;
go
CREATE TABLE [dbo].[CheckTable] (col1 int, col2 int, ok bit)
GO

create unique nonclustered index ixx on dbo.CheckTable(col2) where ok = 1;
go

-- all valid
insert dbo.CheckTable(col1, col2, ok) 
values (1, 1, 0), (2, 2, 1), (3, 0, 0);
go
-- still valid
insert dbo.CheckTable(col1, col2, ok) 
values (4, 1, 1);
go
-- not valid
insert dbo.CheckTable(col1, col2, ok) 
values (5, 1, 1);
go
-- not valid, dup in inserted batch
insert dbo.CheckTable(col1, col2, ok) 
values (6, 8, 1), (7, 8, 1);
go
-- valid
insert dbo.CheckTable(col1, col2, ok) 
values (9, 1, 0);
go

select * from dbo.CheckTable order by col2, ok, col1;
go

整体设计有缺陷,应该换成过滤后的UNIQUE索引。


"This is my go at the problem, and I am struggling to understand how to use the function in the check constraint as the second update passes the check although the function seems work correctly"

检查约束不是 "fired"。

-- original
UPDATE [dbo].[CheckTable] SET ok = 1 WHERE col1 = 1;

-- enforcing col2 update: 1:1 -> no real change
UPDATE [dbo].[CheckTable] SET ok = 1, col2=col2 WHERE col1 = 1;

结果:

Msg 547 Level 16 State 0 Line 1
The UPDATE statement conflicted with the CHECK constraint "chk_col2_ok".

db<>fiddle demo

相关:MSSQL: Update statement avoiding the CHECK constraint