【问题标题】:How do I avoid duplicates on an SQL primary key?如何避免 SQL 主键上的重复?
【发布时间】:2021-04-05 10:20:38
【问题描述】:

这里是初学者。我正在尝试在 SSMS 上创建这个简单的联合表,但我收到了关于主键的重复错误:

消息 2627,第 14 级,状态 1,第 23 行
违反主键约束“PK__FactOffl__B14003C24ECE0589”。无法在对象“dbo.FactOfflineSales”中插入重复键。重复键值为 (43659)。

我做错了什么?

CREATE TABLE FactOfflineSales 
(
    SalesOrderID int NOT NULL PRIMARY KEY,
    SalesOrderNumber nvarchar(20) NOT NULL,
    SalesPersonID int NULL,
    CustomerID int NULL,
    SpecialOfferID int NOT NULL,
    TerritoryID int NOT NULL,
    ProductID int NOT NULL,
    CurrencyRateID int NULL,
    OrderQuantity smallint NULL,
    UnitPrice money NULL,
    SubTotal money NULL,
    TaxAmount money NULL,
    Freight money NULL,
    LineTotal money NULL,
    UnitPriceDiscount float NULL,
    OrderDate datetime NULL,
    ShipDate datetime NULL,
    DueDate datetime NULL,
    OnlineOrderFlag int NULL
);

INSERT INTO FactOfflineSales (
    SalesOrderID
   ,SalesOrderNumber
   ,SalesPersonID
   ,CustomerID
   ,SpecialOfferID
   ,TerritoryID
   ,ProductID
   ,CurrencyRateID
   ,OrderQuantity
   ,UnitPrice
   ,SubTotal
   ,TaxAmount
   ,Freight
   ,LineTotal
   ,UnitPriceDiscount
   ,OrderDate
   ,ShipDate
   ,DueDate
   ,OnlineOrderFlag
)

SELECT 
   SalesOrderHeader.SalesOrderID
   ,SalesOrderHeader.SalesOrderNumber
   ,SalesOrderHeader.SalesPersonID
   ,SalesOrderHeader.CustomerID
   ,SalesOrderDetail.SpecialOfferID
   ,SalesOrderHeader.TerritoryID
   ,SalesOrderDetail.ProductID
   ,SalesOrderHeader.CurrencyRateID
   ,SalesOrderDetail.OrderQty
   ,SalesOrderDetail.UnitPrice
   ,SalesOrderHeader.SubTotal
   ,SalesOrderHeader.TaxAmt
   ,SalesOrderHeader.Freight
   ,SalesOrderDetail.LineTotal
   ,SalesOrderDetail.UnitPriceDiscount 
   ,SalesOrderHeader.OrderDate
   ,SalesOrderHeader.ShipDate
   ,SalesOrderHeader.DueDate
   ,SalesOrderHeader.OnlineOrderFlag
FROM 
    AdventureWorks2019.Sales.SalesOrderHeader SalesOrderHeader
LEFT JOIN 
    AdventureWorks2019.Sales.SalesOrderDetail SalesOrderDetail ON SalesOrderHeader.SalesOrderID = SalesOrderDetail.SalesOrderID;

【问题讨论】:

  • MSSQL 告诉您您将记录加倍,或者您尝试使用SalesOrderId=43659 添加记录。此类记录已经存在并且您需要对其进行过滤,或者使用 SalesOrderDetail LEFT JOIN 在 SalesOrderId 上生成重复项。
  • 我假设您必须首先注意您对 SalesOrderHeader 和 SalesOrderDetail 进行的查询的结果。不应有重复的 SalesOrderID 值。
  • 您的连接应该确定数据库上下文 - 不要在没有充分理由的情况下使用 3 个部分名称。接下来,给您的表提供简短(但不神秘)的别名。保存你的手指 - 你会打字很长时间。
  • 您的查询和新表在逻辑上根本不匹配。是时候备份并考虑(解释)你想要完成的事情了。在不知道这一点的情况下,你可能会得到这个“工作”,只是为了沿着一条最终到达你选择的位置的路径向下移动。如前所述,标题和详细信息之间存在 1:m 关系,因此您尝试使用标题行的 PK 作为表的主键根本行不通。

标签: sql-server duplicates primary-key


【解决方案1】:

您不能在主键列中插入重复值。

您的表 FactOfflineSales 在列 SalesOrderID 上具有主键,该主键将不包含空值和唯一值。但是查看您的查询,salesorderheadersalesorderdetails 表之间似乎存在一对多的关系。所以您的选择查询为单个salesorderid 提供多条记录,这意味着具有相同salesorderid 的多条记录。

FactOfflineSales.salesorderid 中不允许插入重复值。因此,它抛出了一个错误。

最佳做法是使用auto generated sequence 作为主键列。您应该在表中再添加一列并将其声明为主键。(类似于FactOfflineSales_id

【讨论】:

    【解决方案2】:

    您在SalesOrderDetail 上的LEFT JOIN 正在从SalesOrderHeader 中复制出主键所在的行。

    您需要将SalesOrderDetailID 添加到表中,以那个(或那个和SalesOrderID 一起)作为主键

    【讨论】:

      【解决方案3】:

      这里发生了很多事情。让我打开包装。

      1. 当存在多对多时,表之间的连接会有所帮助。这是什么意思?这意味着一个表头有许多明细记录,一个明细记录可以属于许多表头。如果不是这种情况,则不需要连接表。
      2. 如果您真的需要一个连接表,您通常会做以下两件事之一。第一个是在两个表的 ID 上创建一个复合键。如果您使用具有欺骗性的查询加载表(用 DISTINCT 解决),这只会多次唯一。第二个(其他选项)是为主键(如 IDENTITY)创建派生键,然后拥有这两个字段。

      在你学习的过程中,两者都不适用,但你可以玩查询来学习。

      • 查看在 SELECT 中添加 distinct 是否可以消除欺骗。我认为这行不通,但 DISTINCT 很好学。
      • 如果上述方法失败,请在表上添加派生键,然后重新运行查询。为此,您可以使用 IDENTITY(1,1) 作为 PRIMARY KEY 的 Id 字段创建此连接表

      正如其他人已经提到的,您的问题是您在主键上存在欺骗,即销售订单 ID。为什么要骗人?因为每个表头都有多条明细记录。在记录 2 中,它将失败,因为您将标头的 id 作为主键。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多