V T*_*ath 6 sql-server primary-key
我有一张tmp如下所示的表格。
在上表中
IGNORE_DUP_KEY 被设定为 ON和
id列是主键。所述表只有一把钥匙。插入大量数据后,我会得到
重复键被忽略。
由于一些冗余数据的消息。
我想检查尝试插入哪个冗余行。我检查了消息的来源sys.messages。
现在,如何存储由于重复的主键值而在插入尝试发生时失败的行?
会有额外的开销,但一种选择可能是创建一个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)
您可以使用跟踪捕获所有 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)
创建扩展事件
不要选择模板
选择 error_reported 事件
选择 SQL_TEXT 和用户名,以及您想要捕获的任何其他内容
结果