4

我正在尝试对表实施检查约束,以便无法在存在记录的地方插入记录,其中两个列(“Int_1”和“Int_2”)已经具有我们正在尝试插入的值例如:

ID     Name     Int_1     Int_2
1      Dave       1         2

将 (2, Steve, 2, 2) 插入上表是可以的,就像 (3, Mike, 1, 3) 一样,但是不允许插入 Int_1 AND Int_2 已经存在的值,即 (4, Stuart, 1 , 2) 是非法的。

我认为这样定义我的表会起作用:

CREATE TABLE [Table](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](255) NOT NULL,
    [Int_1] [int] NOT NULL,
    [Int_2] [int] NOT NULL,
    CONSTRAINT [chk_Stuff] CHECK (dbo.chk_Ints(Int_1, Int_2)=1))

其中: dbo.chk_Ints 定义为:

CREATE FUNCTION [dbo].[chk_Ints](@Int_1 int,@Int_2 int)
RETURNS int
AS
BEGIN

DECLARE @Result int

IF NOT EXISTS (SELECT * FROM [Table] WHERE Int_1 = @Int_1 AND Int_2 = @Int_2)
BEGIN
    SET @Result = 1
END
ELSE 
BEGIN
    SET @Result = 0
END

RETURN @Result
END

GO

当使用上面的组合时,如果我尝试插入任何记录,SQL 会告诉我我已经破坏了我的检查约束。我可以从表中删除所有行并尝试插入第一条记录,SQL 告诉我我已经打破了我的约束,这是我不可能做到的!

我已经在互联网上搜索了很长一段时间,正在寻找 UDF 依赖于多个表列的检查约束的示例,但无济于事。关于为什么这可能不起作用的任何想法?

提前致谢 :)

4

1 回答 1

14

是的,这可能看起来令人费解,直到您意识到发生了什么,此时它变得非常明显。

该函数针对您尝试插入的行中的值调用。但是想想这个函数是如何被调用的。这是一个调用它的检查约束。

接下来,考虑传递的参数。他们来自哪里?根据定义,检查约束从列Int_1Int_2.

因此,它将它们作为列值传递。但是列值必须属于一行。在这种情况下是哪一行?您要插入的那个!

这意味着此时插入了您的行,只有事务仍处于待处理状态。然而,行在表中这一事实至关重要,因为这就是函数找到并报告1结果的内容。

因此,正在发生的事情是这样的:

  • 您正在尝试插入一行,

  • 该函数看到该行并说具有给定参数的行已经存在,

  • 检查约束通过禁止插入相应地“做出反应”,

  • 插入回滚。

当然,既然您意识到了这一切,就很容易想出不同的检查重复逻辑。基本上,您的函数应该“记住”新行已经在表中,因此它应该尝试确定它在表中的存在是否违反了您想要建立的任何规则。例如,您可以计算与给定参数匹配的行数并查看结果是否不大于 1:

IF (SELECT COUNT(*) FROM [Table] WHERE Int_1 = @Int_1 AND Int_2 = @Int_2) < 2
BEGIN
    SET @Result = 1
END
ELSE 
BEGIN
    SET @Result = 0
END

但是,正如@a_horse_with_no_name 所建议的那样,在此作业的检查约束中使用函数的整个想法远不如仅在两列上添加唯一约束。做这个:

ALTER TABLE [Table]
ADD CONSTRAINT UQ_Table_Int1_Int2 UNIQUE (Int_1, Int_2);

你可以忘记重复。

于 2013-09-24T13:04:08.347 回答