Dan*_*ler 6 sql-server constraint t-sql sql-server-2008-r2
我想让一列唯一,但前提是不同的列是特定值。
考虑下表:
CREATE TABLE [SampleTable]
(
[Id] INTEGER NOT NULL IDENTITY(1,1)
,CONSTRAINT [PK_SampleTable]
PRIMARY KEY ([Id])
,[Code] NVARCHAR(255) NOT NULL
,[Deleted] BIT NOT NULL DEFAULT 0
,[CreatedOn] DATETIME NOT NULL DEFAULT GETDATE()
);
Run Code Online (Sandbox Code Playgroud)
目的是可以通过将[Deleted]
列设置为 1来“删除”项目。
我想要的是将[Code]
列强制为唯一,但仅在未删除的行中。
在数据库级别强制执行数据正确性通常是我的强烈偏好。但是,我过去经常使用这种模式,但一直不确定是否可以在数据库级别强制实施这种约束。截止日期的压力是什么,我从来没有费心去发现。所以我一直只是在应用程序级别强制执行它们。
如果有办法做到这一点,我真的很想知道它是什么。
需要明确的是,我不能只使用组合唯一约束,因为我需要能够支持以下数据:
[ID] [Code] [Deleted] [CreatedOn]
=====================================================================
1 'ABC' 1 Ages ago
2 'ABC' 1 A while ago
3 'ABC' 1 Quite recently, actually
4 'ABC' 0 Just a moment ago!
Run Code Online (Sandbox Code Playgroud)
组合的唯一约束将不起作用,因为就我而言,具有相同代码的三个不同的“已删除”条目是有效的。
我问这个问题是因为最近在集成来自第三方应用程序的数据时,“在应用程序级别强制执行”策略让我感到很恼火。如果数据库直接拒绝错误的集成数据会很好,因为在集成发生之前修复它比在错误发生后清理数据容易得多。
我正在使用 SQL Server 2008 R2。但我不介意升级,如果我必须获得这个功能,无论如何我一直想升级。
Aaron 很快就回答了这个问题。我需要使用过滤的唯一索引。
以下代码演示了解决方案。
IF EXISTS (SELECT * FROM SYS.TABLES WHERE [name] = 'SampleTable')
BEGIN
PRINT 'Dropping Table [SampleTable]';
DROP TABLE [SampleTable];
END;
GO
PRINT 'Creating Table [SampleTable]';
CREATE TABLE [SampleTable]
(
[Id] INTEGER NOT NULL IDENTITY(1,1)
,CONSTRAINT [PK_SampleTable]
PRIMARY KEY ([Id])
,[Code] NVARCHAR(255) NOT NULL
,[Deleted] BIT NOT NULL DEFAULT 0
,[CreatedOn] DATETIME NOT NULL DEFAULT GETDATE()
);
CREATE UNIQUE INDEX
[UNQ_SampleTable_Code]
ON
[SampleTable]([Code])
WHERE
([Deleted] = 0);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 1);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 1);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 1);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 1);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 0);
INSERT INTO [SampleTable] ([Code],[Deleted]) VALUES ('ABC', 0);
UPDATE [SampleTable] SET [Deleted] = 0 WHERE [Id] = 1;
SELECT * FROM [SampleTable];
Run Code Online (Sandbox Code Playgroud)
由于过滤索引,第六次(最终)插入和更新都失败了。
以后我会好好利用这个的。谢谢阿龙!
Aar*_*and 12
当您有一个唯一约束并且只想应用于行的子集时,您可以使用唯一的、过滤的索引来强制执行此操作。在这种情况下,似乎对您有用的索引是:
CREATE UNIQUE INDEX [UNQ_SampleTable_Code]
ON dbo.[SampleTable]([Code])
WHERE ([Deleted] = 0);
Run Code Online (Sandbox Code Playgroud)
这确保了Code
对于Deleted
为 0 的行只能存在一个不同的值,但是对于为 1 的行可以存在重复项Deleted
。通常这也有助于某些查询的性能,因为您通常只对活动行(而不是软删除),但如果这不包括查询,您可能需要考虑将列添加到 INCLUDE 子句(如果认为查找成本过高,SQL Server 可能会选择扫描聚集索引或其他索引)。