SQL Server:检查约束中的用户定义函数

时间:2018-06-18 13:12:41

标签: sql sql-server

我有一张表来跟踪数据是否已经转移了一段时间。这是一个简短的例子(遗漏了很多列,但这应该足够了):

+----+--------+------------+------+
| Id | DealId |    Date    | Sent |
+----+--------+------------+------+
|  1 |      1 | 2018-01-01 |    1 |
|  2 |      1 | 2018-02-01 |    1 |
|  3 |      1 | 2018-03-01 |    0 |
|  4 |      2 | 2018-01-01 |    1 |
|  5 |      2 | 2018-02-01 |    1 |
|  6 |      2 | 2018-03-01 |    0 |
|  7 |      1 | 2018-04-01 |    0 |
+----+--------+------------+------+

我想创建一个阻止插入最后一条记录的检查约束。不应该允许每个DealId有多个Sent = 0行。 换句话说:如果已经有Sent = 0的Deal = 0,那么甚至不可能为DealId插入一行。

这是带有约束的表脚本:

    CREATE TABLE [Mrd].[Snapshot]
    (
        [Id] INT IDENTITY(1,1) CONSTRAINT [PK_Mrd_Snapshot_Id] PRIMARY KEY,
        [DealId] INT NOT NULL,
        [Date] DATETIME NOT NULL,
        [Sent] BIT NOT NULL CONSTRAINT [DF_Mrd_Snapshot_Sent] DEFAULT 0,

        CONSTRAINT [CK_Mrd_Snapshot_Sent]
        CHECK ([Function].[ValidateSent]([DealId]) = 1)
)

这是函数脚本:

CREATE FUNCTION [Function].[ValidateSent]
(
    @DealId INT
)
RETURNS BIT
AS
BEGIN
    IF ((SELECT COUNT(*) FROM [Mrd].[Snapshot] WHERE [DealId] = @DealId AND [Sent] = 0) = 0)
    BEGIN
        RETURN 1;
    END;    

    RETURN 0;
END

快速复制粘贴的架构创建:

CREATE SCHEMA [Function]

有了这个实现,我不能插入任何具有Sent = 0的行。 它会在检查约束中引发冲突错误。

关于我在约束或函数中做错了什么的任何建议? 或者也许有更好的方法来进行此检查?

谢谢!

2 个答案:

答案 0 :(得分:5)

我个人赞成在这种情况下使用唯一索引而不是约束。我发现我想要的列是独一无二的我可能想要一个索引,所以2只1石头。所以我会这样做:

CREATE UNIQUE NONCLUSTERED INDEX [uidx_dealid_sent] ON [dbo].[Snapshot]
(
    [dealid] ASC,
    [sent] ASC
)
WHERE ([sent]=0)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

复合唯一索引执行您要执行的操作:您无法在已存在特定dealid和特定sent的位置插入行。正如Larnu指出的那样,这应该是一个过滤索引(适用于MSSQL 2008及更高版本)。请注意WHERE ([sent]=0),这应该满足您只需要在sent = 0时应用规则的要求。

答案 1 :(得分:1)

对于它的价值,你得到代码错误的原因是因为当CHECK CONSTRAINT调用一个函数时,函数代码假定已经创建/插入了触发调用的行/更新。

因此,在您的函数中,您正在测试是否存在具有Sent=0的零行。行已插入COUNTS作为表中的一行,因此如果您只允许零行拥有Sent=0,那么您将永远无法插入Sent=0的单行。

要执行您想要的操作,您应该只允许每个ID占一行Sent=0

相关问题