如何解决SQL Server的唯一约束是“无声的”还是引发异常?
我想防止在表中插入重复的值。首先,我添加了代码来检查该值是否已经存在于数据库中,但是如果我可以在DDL级别上阻止它,那似乎是很多开销/浪费时间。
因此我找到了this并从中更改了我的一张桌子(作为示例):
CREATE TABLE [dbo].[ACTORS]
(
[Id] INT IDENTITY (1,1) NOT NULL,[ActorId] CHAR(9) NOT NULL,[Actor] VARCHAR(50) NOT NULL,PRIMARY KEY CLUSTERED ([Id] ASC),);
对此:
CREATE TABLE [dbo].[ACTORS]
(
[Id] INT IDENTITY (1,CONSTRAINT [CK_ACTORS_Column]
UNIQUE NONCLUSTERED ([ActorId] ASC)
);
我希望约束条件阻止第二个相同的ActorId
而不发牢骚。 IOW,绕开它,不要告诉我,不要停止应用程序或引发异常。
这是(静默地)工作的方式吗,还是会引发异常?
解决方法
让我们尝试一下
insert into actors (actorid,actor) values('foo','bar');
-- 1 row affected
insert into actors (actorid,'baz');
-- Msg 2627 Level 14 State 1 Line 1
-- Violation of UNIQUE KEY constraint 'CK_ACTORS_Column'.
-- Cannot insert duplicate key in object 'dbo.ACTORS'. The duplicate key value is (foo ).
唯一约束违反确实会引发错误。这样数据库就可以让您知道出了什么问题。
与许多其他数据库(MySQL,Postgres,SQLite ...)不同,SQL Server内置的(我所知道的)无选项可以忽略这种错误。一种解决方法是用insert
和一个子查询重写not exists
:
insert into actors (actorid,actor)
select v.*
from (values ('foo','bar')) v(actorid,actor)
where not exists (select 1 from actor a where a.actorid = v.actorid)
另一种选择是merge
语句:
merge into actors a
using (values ('foo',actor)
on v.actorid = a.actorid
when not matched then insert (actorid,actor)
values (v.actorid,v.actor)
,
@GMB在他的回答中写道:“ SQL Server内置了(我知道)没有选择来忽略此类错误”。
正如@a_horse_with_no_name在评论中指出的,此处有一个与索引相关的IGNORE_DUP_KEY
选项。
您说:
我希望约束阻止第二个相同的ActorId 抱怨。 IOW,绕开它,不要告诉我, 不要停止应用程序或引发异常。
可以通过此选项实现。
首先,我应该指出,当您创建唯一约束时
CONSTRAINT [CK_ACTORS_Column] UNIQUE NONCLUSTERED ([ActorId] ASC)
引擎将创建一个唯一索引以强制执行约束。约束是逻辑概念,索引是概念的物理实现。
您可以通过仅创建索引(唯一索引)来达到相同的效果。创建索引时,您可以指定各种选项,包括IGNORE_DUP_KEY
选项。
IGNORE_DUP_KEY = {开|关闭}
指定当 插入操作尝试将重复的键值插入唯一的 指数。 IGNORE_DUP_KEY选项仅适用于插入操作 在创建或重建索引之后。该选项在以下情况下无效 执行CREATE INDEX,ALTER INDEX或UPDATE。默认值为OFF。
打开:插入重复的键值时将出现警告消息 成为唯一索引。只有违反唯一性约束的行 将失败。
关闭:插入重复的键值时将出现错误消息 成为唯一索引。整个INSERT操作将回滚。
默认情况下,此选项为OFF
,因此,尝试插入重复的键值将失败并显示错误。服务器将回滚INSERT操作并将此错误消息发送到您的应用程序,这将取决于您的应用程序如何处理它。如果您的应用程序不期望它,则可能会引发一些异常。
如果将此选项设置为ON
,则您的应用程序将不再收到错误消息。它将收到警告消息,大多数应用程序通常会忽略该消息。因此,看起来服务器将静默地忽略重复值,而仅插入那些不重复的值。
安静地忽略问题很少是一种期望的行为,但是如果您真的知道自己在做什么,就可以做到。
这是一个简短的演示。
让我们从您的桌子开始
CREATE TABLE [dbo].[ACTORS]
(
[Id] INT IDENTITY (1,1) NOT NULL,[ActorId] CHAR(9) NOT NULL,[Actor] VARCHAR(50) NOT NULL,PRIMARY KEY CLUSTERED ([Id] ASC),);
选项1.默认。 IGNORE_DUP_KEY = OFF
CREATE UNIQUE NONCLUSTERED INDEX [IX_ActorID] ON [dbo].[ACTORS]
(
[ActorId] ASC
) WITH (IGNORE_DUP_KEY = OFF)
该表为空。让我们尝试插入一些值。
insert into actors (actorid,'bar');
-- (1 row affected)
让我们尝试插入重复的值:
insert into actors (actorid,'baz');
--Msg 2601,Level 14,State 1,Line 4
--Cannot insert duplicate key row in object 'dbo.ACTORS' with unique index 'IX_ActorID'.
--The duplicate key value is (foo ).
--The statement has been terminated.
让我们尝试在带有一些重复项的单个语句中插入多个值:
insert into actors (actorid,actor) values
('foo','baz'),('fo2',('fo3',('fo4',Line 16
--Cannot insert duplicate key row in object 'dbo.ACTORS' with unique index 'IX_ActorID'.
--The duplicate key value is (foo ).
--The statement has been terminated.
现在让我们看看表中的内容。
SELECT * FROM Actors;
+----+-----------+-------+
| Id | ActorId | Actor |
+----+-----------+-------+
| 1 | foo | bar |
+----+-----------+-------+
仅第一个INSERT
语句成功,并且仅插入了一行。
现在,清理。
DROP INDEX [IX_ActorID] ON [dbo].[ACTORS];
TRUNCATE TABLE dbo.Actors;
选项2。IGNORE_DUP_KEY = ON
CREATE UNIQUE NONCLUSTERED INDEX [IX_ActorID] ON [dbo].[ACTORS]
(
[ActorId] ASC
) WITH (IGNORE_DUP_KEY = ON)
该表为空。让我们尝试插入一些值。
insert into actors (actorid,'baz');
--Duplicate key was ignored.
--(0 rows affected)
如您所见,现在这不是错误消息。只是警告“重复键被忽略。”
让我们尝试在带有一些重复项的单个语句中插入多个值:
insert into actors (actorid,'baz1'),'baz2'),'baz3'),'baz4'),'baz5'),'baz6'),'baz7');
--Duplicate key was ignored.
--(3 rows affected)
在这里您可以看到7行中仅插入了3行。
现在让我们看看表中的内容。
SELECT * FROM Actors;
+----+-----------+-------+
| Id | ActorId | Actor |
+----+-----------+-------+
| 1 | foo | bar |
| 4 | fo2 | baz2 |
| 6 | fo3 | baz4 |
| 9 | fo4 | baz7 |
+----+-----------+-------+
您可以看到最后一个INSERT
语句插入了非重复值。另外,请仔细查看ID
列中的值。 IDENTITY
值之间存在间隙,因为它们是为尝试插入的每一行生成的,并且其中某些行被唯一索引拒绝。
总体而言,此索引选项主要用于以下情况:需要在单个INSERT
语句中批量插入很多行,但是您希望其中的 some 行可能违反唯一约束。您不希望整个大型INSERT
语句失败,而只希望忽略很少的违规行。如果没有此选项,则必须尝试逐行,逐行地插入值,这可能比单个INSERT
语句要慢得多。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。