Sql 为什么按主键分组的插入会抛出主键约束冲突错误?
我有一个insert语句抛出主键错误,但我不知道怎么可能插入重复的键值 首先,我创建一个带有主键的临时表Sql 为什么按主键分组的插入会抛出主键约束冲突错误?,sql,sql-server,database,Sql,Sql Server,Database,我有一个insert语句抛出主键错误,但我不知道怎么可能插入重复的键值 首先,我创建一个带有主键的临时表 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED //Note: I've tried committed and uncommited, neither materially affects the behavior. See screenshots below for proof. IF (OBJECT_ID('TEMPDB..#P'))
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED //Note: I've tried committed and uncommited, neither materially affects the behavior. See screenshots below for proof.
IF (OBJECT_ID('TEMPDB..#P')) IS NOT NULL DROP TABLE #P;
CREATE TABLE #P(idIsbn INT NOT NULL PRIMARY KEY, price SMALLMONEY, priceChangedDate DATETIME);
然后我从prices表中提取价格,按idIsbn分组,idIsbn是temp表中的主键
INSERT INTO #P(idIsbn, price, priceChangedDate)
SELECT idIsbn ,
MIN(lowestPrice) ,
MIN(priceChangedDate)
FROM Price p
WHERE p.idMarketplace = 3100
GROUP BY p.idIsbn
我知道idIsbn根据定义进行分组使其具有独特性。价格表中的idIsbn为:[idIsbn][int]不为空
但每隔一段时间,当我运行此查询时,就会出现以下错误:
Violation of PRIMARY KEY constraint 'PK__#P________AED35F8119E85FC5'. Cannot insert duplicate key in object 'dbo.#P'. The duplicate key value is (1447858).
注意:我有很多关于时间的问题。我将选择此语句,按F5,不会出现错误。然后我会再做一次,它会失败,然后我会一次又一次地运行它,它会成功几次,然后再次失败。我想我想说的是,我找不到它何时会成功,何时不会成功的模式
如果A我在插入表之前刚刚创建了一个全新的表,B我按设计为主键的列进行分组,那么如何插入重复的行
现在,我正在使用IGNORE_DUP_KEY=ON解决问题,但我真的很想知道问题的根本原因
以下是我在SSMS窗口中实际看到的内容。没有更多,也没有更少:
@@版本为:
Microsoft SQL Server 2008 (SP3) - 10.0.5538.0 (X64)
Apr 3 2015 14:50:02
Copyright (c) 1988-2008 Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
以及两个非聚集索引:
CREATE NONCLUSTERED INDEX [IX_Price_idMarketplace_INC_idIsbn_lowestPrice_priceDate] ON [dbo].[Price]
(
[idMarketplace] ASC
)
INCLUDE ( [idIsbn],
[lowestPrice],
[priceDate]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Price_idMarketplace_priceChangedDate_INC_idIsbn_lowestPrice] ON [dbo].[Price]
(
[idMarketplace] ASC,
[priceChangedDate] ASC
)
INCLUDE ( [idIsbn],
[lowestPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
你没有提供你的桌子结构 这是一个复制,其中包含一些假设的细节,导致了read committed NB的问题:现在您提供了我在您的案例中看到的定义。对priceChangedDate列的更新将在IX_Price_idMarketplace_priceChangedDate_INC_idIsbn_lowestPrice index中移动行(如果正在查找的话) 连接1设置表 连接2 并发数据修改,将一行从搜索范围3100,1的开始移动到31002001的结束,然后再次重复移动。 连接3使用唯一约束插入临时表 该计划没有聚合,因为idIsbn上存在唯一约束idIsbn、idMarketplace上的唯一约束也会起作用,因此可以优化group by,因为没有重复值 但在读提交隔离级别,共享行锁会在读取行时立即释放。因此,行可以移动位置,并通过相同的搜索或扫描进行第二次读取
索引ix没有显式地将SomeKey包含为辅助键列,但由于它不是唯一的,SQL Server会在后台静默地包含群集键,因此更新该列值可以移动其中的行。源表中“idsku”的数据类型是什么?不知道,如果可能是不同的数据类型,则可能是不同的实际值转换为相同的int。能否发布显示创建和插入的整个脚本?是否可能是创建临时表时出错?因为临时表已经存在,因此插入与现有数据冲突?@GSerg如果计划中没有任何一种形式的聚合。我想一定有一个独特的约束,确保没有重复,所以它得到了优化。所以并发活动肯定会导致这种情况。如果一个键列被更新,使得一行在被查找的索引中向上移动并被读取两次,那么这种情况也可能在读取提交时发生。我在一致性方面很灵活,但我已经多次看到这个问题,我总是使用ignore_dup_键,但我决定今天就真正尝试理解它,而你已经来解救了。这真是太棒了。再次感谢。回答得很好,它真的帮助我理解了这个潜在的问题。我做过很多这样的查询,但几乎都是从单独的报表数据库/仓库中进行的,在这些数据库/仓库中,数据基本上是只读的,所以我在现实生活中从未碰到过它。
CREATE NONCLUSTERED INDEX [IX_Price_idMarketplace_INC_idIsbn_lowestPrice_priceDate] ON [dbo].[Price]
(
[idMarketplace] ASC
)
INCLUDE ( [idIsbn],
[lowestPrice],
[priceDate]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Price_idMarketplace_priceChangedDate_INC_idIsbn_lowestPrice] ON [dbo].[Price]
(
[idMarketplace] ASC,
[priceChangedDate] ASC
)
INCLUDE ( [idIsbn],
[lowestPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
USE tempdb;
CREATE TABLE Price
(
SomeKey INT PRIMARY KEY CLUSTERED,
idIsbn INT IDENTITY UNIQUE,
idMarketplace INT DEFAULT 3100,
lowestPrice SMALLMONEY DEFAULT $1.23,
priceChangedDate DATETIME DEFAULT GETDATE()
);
CREATE NONCLUSTERED INDEX ix
ON Price(idMarketplace)
INCLUDE (idIsbn, lowestPrice, priceChangedDate);
INSERT INTO Price
(SomeKey)
SELECT number
FROM master..spt_values
WHERE number BETWEEN 1 AND 2000
AND type = 'P';
USE tempdb;
WHILE 1=1
BEGIN
UPDATE Price SET SomeKey = 2001 WHERE SomeKey = 1
UPDATE Price SET SomeKey = 1 WHERE SomeKey = 2001
END
USE tempdb;
CREATE TABLE #P
(
idIsbn INT NOT NULL PRIMARY KEY,
price SMALLMONEY,
priceChangedDate DATETIME
);
WHILE 1 = 1
BEGIN
TRUNCATE TABLE #P
INSERT INTO #P
(idIsbn,
price,
priceChangedDate)
SELECT idIsbn,
MIN(lowestPrice),
MIN(priceChangedDate)
FROM Price p
WHERE p.idMarketplace = 3100
GROUP BY p.idIsbn
END