我有一张表来跟踪数据是否已经转移了一段时间。这是一个简短的例子(遗漏了很多列,但这应该足够了):
+----+--------+------------+------+
| 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的行。 它会在检查约束中引发冲突错误。
关于我在约束或函数中做错了什么的任何建议? 或者也许有更好的方法来进行此检查?
谢谢!
答案 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
。