如何在 SQL Server 上打开 IGNORE_DUP_KEY 时获取被忽略的行?

V T*_*ath 6 sql-server primary-key

我有一张tmp如下所示的表格。

在此处输入图片说明

在上表中

  • IGNORE_DUP_KEY 被设定为 ON

  • id列是主键。

所述表只有一把钥匙。插入大量数据后,我会得到

重复键被忽略。

由于一些冗余数据的消息。

我想检查尝试插入哪个冗余行。我检查了消息的来源sys.messages

现在,如何存储由于重复的主键值而在插入尝试发生时失败的行?

Sco*_*red 6

会有额外的开销,但一种选择可能是创建一个instead of insert触发器,该触发器将首先检查重复项并将其路由到另一个表。

--demo setup
set nocount on
DROP TABLE IF EXISTS [dbo].[TestTable]
CREATE TABLE [dbo].[TestTable](
    [ID] [int] NOT NULL,
    [ExtraInformation] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Employee_ID] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
) with (IGNORE_DUP_KEY = ON)
) ON [PRIMARY] 
GO

DROP TABLE IF EXISTS [dbo].[TestTableIgnoredDups]
CREATE TABLE [dbo].[TestTableIgnoredDups](
    [ID] [int] NOT NULL,
    [ExtraInformation] [varchar](50) NOT NULL
)
 ON [PRIMARY]
GO

--create INSTEAD OF trigger
CREATE TRIGGER TestTable_InsteadOfInsert ON dbo.TestTable
INSTEAD OF INSERT
AS
BEGIN
    --select rows to be inserted into #temp
    SELECT *
    INTO #temp
    FROM inserted

    --insert rows to TestTableIgnoredDups where primary key already exists
    INSERT INTO TestTableIgnoredDups
    SELECT t.*
    FROM #temp t
    JOIN TestTable tt
        ON tt.id = t.id

    --delete the duplicate rows from #temp
    DELETE t
    FROM #temp t
    JOIN TestTable tt
        ON tt.id = t.id

    --insert rows to TestTableIgnoredDups where duplicates
    --exist on the inserted virtual table, but not necessarily on TestTable
    ;WITH DupsOnInserted
    AS (
        SELECT id
            ,count(*) AS cnt
        FROM #temp
        GROUP BY id
        HAVING count(*) > 1
        )
    INSERT INTO TestTableIgnoredDups
    SELECT t.*
    FROM #temp t
    JOIN DupsOnInserted doi
        ON doi.id = t.id;

    ;WITH DupsOnInserted
    AS (
        SELECT id
            ,count(*) AS cnt
        FROM #temp
        GROUP BY id
        HAVING count(*) > 1
        )
    DELETE t
    FROM #temp t
    JOIN DupsOnInserted doi
        ON doi.id = t.ID

    --insert the remaining rows to TestTable
    INSERT INTO TestTable
    SELECT *
    FROM #temp
END
GO




--verify by trying to insert a duplicate row 
insert into testtable(id,ExtraInformation) values(1,'RowOne')
insert into testtable(id,ExtraInformation) values(1,'RowOneDup')

select * from TestTable
select * from TestTableIgnoredDups
Run Code Online (Sandbox Code Playgroud)

来自测试表的数据

| ID | ExtraInformation |
|----|------------------|
| 1  | RowOne           |
Run Code Online (Sandbox Code Playgroud)

来自 TestTableIgnoreDups 的数据

| ID | ExtraInformation |
|----|------------------|
| 1  | RowOneDup        |
Run Code Online (Sandbox Code Playgroud)


Ran*_*gen 4

您可以使用跟踪捕获所有 PK 异常,也可以捕获哪些 sql 语句使用扩展事件触发重复键消息。

在以下示例中,它向您展示了如何使用探查器跟踪捕获“异常”,或使用扩展事件捕获触发重复键消息的执行 SQL 文本。

区别在于“异常”跟踪为每个违规获取一行,我们可以将其记录到文件中,然后从该文件中读取。

你能捕捉到什么?

在返回用户错误信息之前,内部发生了PK违规:

在此输入图像描述

这又给出了重复的键值:

Violation of PRIMARY KEY constraint 'PK__ignore_d__3BD0198E9F9BACEA'. Cannot insert duplicate key in object 'dbo.ignore_dup_key'. The duplicate key value is (1).
Run Code Online (Sandbox Code Playgroud)

虽然用户看不到此消息,但我们可以通过跟踪或扩展事件捕获这些消息。

在此输入图像描述


表中异常的探查器跟踪

在此输入图像描述

添加过滤器

在此输入图像描述

捕捉 PK 违规行为,即使 IGNORE_DUP_KEY = ON

在此输入图像描述

捕获的错误消息

Violation of PRIMARY KEY constraint 'PK__ignore_d__3BD0198E9F9BACEA'. Cannot insert duplicate key in object 'dbo.ignore_dup_key'. The duplicate key value is (1).
Run Code Online (Sandbox Code Playgroud)

这里的问题是它很快就会变得混乱,因为它为每个失败值提供一条记录,所以如果 1 和 2 已经存在

INSERT INTO ignore_dup_key(a) VALUES(1), (2)
Run Code Online (Sandbox Code Playgroud)

它在探查器跟踪中给出了两个新的异常:

1)

Violation of PRIMARY KEY constraint 'PK__ignore_d__3BD0198E9F9BACEA'. Cannot insert duplicate key in object 'dbo.ignore_dup_key'. The duplicate key value is (1).
Run Code Online (Sandbox Code Playgroud)

2)

Violation of PRIMARY KEY constraint 'PK__ignore_d__3BD0198E9F9BACEA'. Cannot insert duplicate key in object 'dbo.ignore_dup_key'. The duplicate key value is (2).
Run Code Online (Sandbox Code Playgroud)

将其保存到表中

在此输入图像描述

从新表中选择

SELECT *
  FROM [my_test].[dbo].[FindViolations];
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

因此,当插入 1000 个重复 ID 时,日志记录将保存 1000 条额外记录

  TRUNCATE TABLE [my_test].[dbo].[FindViolations];

  INSERT INTO ignore_dup_key(a) 
  select a from ignore_dup_key; -- 1000 duplicate records

  SELECT COUNT(*) from [my_test].[dbo].[FindViolations];
Run Code Online (Sandbox Code Playgroud)

结果

(No column name)
1000
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述 ETC。


创建扩展事件

在此输入图像描述

不要选择模板

在此输入图像描述

选择 error_reported 事件

在此输入图像描述

选择 SQL_TEXT 和用户名,以及您想要捕获的任何其他内容

在此输入图像描述

结果

在此输入图像描述

您还可以添加一个过滤器,以过滤掉非重复的键错误 在此输入图像描述