【问题标题】:How can this query violate primary key?此查询如何违反主键?
【发布时间】:2016-05-18 14:59:46
【问题描述】:

假设我有一个空的ProductLocalizations 表,其中有一个由ProductIdLanguageCode 字段组成的复合主键,我执行以下查询。

INSERT INTO dbo.ProductLocalizations (ProductId, LanguageCode, Name, Description) 
SELECT ProductId, LanguageCode1, Name1, Description1 FROM dbo.BulkProducts 
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode2, Name2, Description2 FROM dbo.BulkProducts
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode3, Name3, Description3 FROM dbo.BulkProducts
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode4, Name4, Description4 FROM dbo.BulkProducts
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) 

根据我的(有限的)SQL 知识,因为查询包含

WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations)

子句,不应该有任何方法可以插入重复(无效)的记录。但是,执行此操作时,我会收到

消息 2627,级别 14,状态 1,第 1 行违反主键 约束“PK_dbo.ProductLocalizations”。无法插入重复键 在对象“dbo.ProductLocalizations”中。重复键值为 (29977,德)。声明已终止。

我错过了什么?

【问题讨论】:

  • 如果BulkProductsBulkProdu 有共同的ProductId ex:100 而在ProductLocalizations 表中不存在怎么办。
  • 另外,您似乎在ProductLocalizations 表中有复合主键,因此您需要检查ProductId + another column 是否存在
  • 检查LanguageCode1 是否与LanguageCode2 或其他ProductId 不同
  • @Prdp BulkProdu 实际上是 BulkProducts,抱歉打错了。 @Squirrel 检查,我的 BulkProducts 表中只有几条记录,它们显然不一样。
  • 我的意思是这也是一种可能性。

标签: sql sql-server database


【解决方案1】:

您的表中有一个复合主键,它由ProductIdLanguageCode 组成。您需要修改WHERE 子句以使用NOT EXISTS。另外,seeing as you're unpivoting the table, you may want to use CROSS APPLY to improve the speed

INSERT INTO dbo.ProductLocalizations (ProductId, LanguageCode, Name, Description) 
SELECT
    bp.ProductId, t.LanguageCode, t.Name, t.Description
FROM dbo.BulkProducts bp
CROSS APPLY(
    VALUES  (LanguageCode1, Name1, Description1),
            (LanguageCode2, Name2, Description2),
            (LanguageCode3, Name3, Description3),
            (LanguageCode4, Name4, Description4)
) t(LanguageCode, Name, Description)
WHERE NOT EXISTS(
    SELECT 1
    FROM dbo.ProductLocalizations p
    WHERE
        p.ProductId = bp.ProductId
        AND p.LanguageCode = t.LanguageCode
)

如果你不能使用CROSS APPLY,这里是改进的UNION ALL版本:

INSERT INTO dbo.ProductLocalizations (ProductId, LanguageCode, Name, Description) 
SELECT * 
FROM (
    SELECT ProductId, LanguageCode1, Name1, Description1 FROM dbo.BulkProducts bp UNION ALL
    SELECT ProductId, LanguageCode2, Name2, Description2 FROM dbo.BulkProducts bp UNION ALL
    SELECT ProductId, LanguageCode3, Name3, Description3 FROM dbo.BulkProducts bp UNION ALL
    SELECT ProductId, LanguageCode4, Name4, Description4 FROM dbo.BulkProducts bp 
) t (ProductId, LanguageCode, Name, Description)
WHERE NOT EXISTS(
    SELECT 1
    FROM dbo.ProductLocalizations p
    WHERE
        p.ProductId = t.ProductId
        AND p.LanguageCode = t.LanguageCode
)

【讨论】:

  • 谢谢。什么情况下不能使用CROSS APPLY
  • @iuliu.net,如果您使用的是旧版本的 SQL Server。如果我没记错的话,APPLY 在 SQL Server 2005 中可用。
【解决方案2】:
SELECT ProductId, LanguageCode1, Name1, Description1 FROM dbo.BulkProducts 
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode2, Name2, Description2 FROM dbo.BulkProdu
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode3, Name3, Description3 FROM dbo.BulkProdu
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) UNION 
SELECT ProductId, LanguageCode4, Name4, Description4 FROM dbo.BulkProdu
WHERE ProductId NOT IN (SELECT ProductId FROM dbo.ProductLocalizations) 

您的选择查询返回 productID 和 languagecode 列的重复值

【讨论】:

  • 可能值得澄清的是查询没有返回完全重复的行,因为否则UNION被破坏 - 但它们重复的仅考虑 ProductIdLanguageCode 列时。
  • @Damien_The_Unbeliever 修改了我的答案。感谢您的建议。
猜你喜欢
  • 1970-01-01
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2012-07-15
  • 1970-01-01
相关资源
最近更新 更多