【问题标题】:Violation of PRIMARY KEY constraint. Cannot insert duplicate key in object违反 PRIMARY KEY 约束。无法在对象中插入重复键
【发布时间】:2016-03-16 17:59:05
【问题描述】:

我继承了一个项目,但遇到了一个不知道如何修复的 SQL 错误。

在电子商务网站上,代码将订单发货信息插入到另一个数据库表中。

以下是将信息插入表格的代码:

string sql = "INSERT INTO AC_Shipping_Addresses   
(pk_OrderID, FullName, Company, Address1, Address2, City, Province, PostalCode, CountryCode, Phone, Email, ShipMethod, Charge_Freight, Charge_Subtotal)  
VALUES (" + _Order.OrderNumber;
sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToCompany == "")
{
  sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
}
else
{
  sql += ", '" + _Order.Shipments[0].ShipToCompany.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].Address.Address1.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Address2.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.City.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Province.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.PostalCode.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Country.Name.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Phone.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToEmail == "")
{
  sql += ",'" + _Order.BillToEmail.Replace("'", "''") + "'";
}
else
{
  sql += ",'" + _Order.Shipments[0].ShipToEmail.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].ShipMethod.Name.Replace("'", "''") + "'";
sql += ", " + shippingAmount;
sql += ", " + _Order.ProductSubtotal.ToString() + ")";
bll.dbUpdate(sql);

它工作正常,但它也输出以下SQL错误:

违反主键约束“PK_AC_Shipping_Addresses”。无法插入 对象“dbo.AC_Shipping_Addresses”中的重复键。重复键值 是 (165863)。

通过阅读类似的问题,我似乎应该在声明中声明ID。

正确吗?我将如何调整代码来解决这个问题?

【问题讨论】:

  • 步骤 1) 在运行之前找出 sql 的值是多少。步骤 0) 更改为对查询使用绑定值而不是串联。谷歌“SQL注入”的原因。
  • 你传递给主键的值是什么(大概是“pk_OrderID”)?您可以将其设置为自动递增,然后复制值永远不会有问题 - 数据库会处理这个问题。如果您需要自己指定一个值,则需要编写代码来确定该字段的最大值是多少,然后将其递增。
  • 您的唯一关键字段是什么?看起来您的 OrderNumber 是重复的,您的表中可能已经有 order# 165863 并且您正在尝试插入重复
  • 如果 PK 值已经存在,那么您只需更新它,否则您必须在插入新值之前删除先前的值:string sql = "DELETE FROM AC_Shipping_Addresses where pk_OrderID = " + _Order.OrderNumber;

标签: sql-server sql-server-2008 primary-key


【解决方案1】:

当我尝试使用 EntityFramework 插入新记录时,我在恢复的数据库上遇到了同样的错误。事实证明,Indentity/Seed 搞砸了。

使用 reseed 命令修复它。

DBCC CHECKIDENT ('[Prices]', RESEED, 4747030);GO

【讨论】:

    【解决方案2】:

    我很确定pk_OrderIDAC_Shipping_Addresses 的PK

    您正在尝试通过_Order.OrderNumber 插入重复项?

    做一个

    select * from AC_Shipping_Addresses where pk_OrderID = 165863;
    

    select count(*) ....

    很确定你会得到一行返回。

    它告诉您您已经在使用pk_OrderID = 165863 并且不能有具有该值的另一​​行。

    如果有行就不要插入

    insert into table (pk, value) 
    select 11 as pk, 'val' as value 
    where not exists (select 1 from table where pk = 11)
    

    【讨论】:

    • 我正在尝试添加一些代码来首先检查该值。这看起来是否正确?: string exists = "SELECT 1 from AC_Shipping_Addresses where pk_OrderID = " _Order.OrderNumber; if (exists > 0) { // 已经存在 } else { // 插入订单数据
    • 是的,理论上可行。但我添加了一些我更喜欢的东西。
    • 这不是一个好的答案,开发人员将主列确定为标识自动增量列!这是一个转义码而不是解决方案:)
    【解决方案3】:

    您传递给主键的值是什么(大概是“pk_OrderID”)?您可以将其设置为自动递增,然后复制值永远不会有问题 - 数据库会处理这个问题。如果您需要自己指定一个值,则需要编写代码来确定该字段的最大值是多少,然后将其递增。

    如果您有一个名为“ID”的列或未在查询中显示的列,那么只要将其设置为自动增量就可以了 - 但它可能不是,或者您不应该收到该错误消息。此外,您最好编写一个更易于使用的查询并使用参数。正如这位九岁的小伙子推断的那样,如果您只是简单地输入用户输入的值,您的数据库就会受到 SQL 注入攻击。例如,你可以有这样的方法:

    internal static int GetItemIDForUnitAndItemCode(string qry, string unit, string itemCode)
    {
        int itemId;
        using (SqlConnection sqlConn = new SqlConnection(ReportRunnerConstsAndUtils.CPSConnStr))
        {
            using (SqlCommand cmd = new SqlCommand(qry, sqlConn))
            {
                cmd.CommandType = CommandType.Text;
                cmd.Parameters.Add("@Unit", SqlDbType.VarChar, 25).Value = unit;
                cmd.Parameters.Add("@ItemCode", SqlDbType.VarChar, 25).Value = itemCode;
                sqlConn.Open();
                itemId = Convert.ToInt32(cmd.ExecuteScalar());
            }
        }
        return itemId;
    }
    

    ...就是这样称呼的:

    int itemId = SQLDBHelper.GetItemIDForUnitAndItemCode(GetItemIDForUnitAndItemCodeQuery, _unit, itemCode);
    

    您不必这样做,但我将查询单独存储:

    public static readonly String GetItemIDForUnitAndItemCodeQuery = "SELECT PoisonToe FROM Platypi WHERE Unit = @Unit AND ItemCode = @ItemCode";
    

    您可以通过(伪代码)验证您不会插入已经存在的值:

    bool alreadyExists = IDAlreadyExists(query, value) > 0;
    

    查询类似于“SELECT COUNT FROM TABLE WHERE BLA = @CANDIDATEIDVAL”,值是您可能要插入的 ID:

    if (alreadyExists) // keep inc'ing and checking until false, then use that id value
    

    贾斯汀想知道这是否可行:

    string exists = "SELECT 1 from AC_Shipping_Addresses where pk_OrderID = " _Order.OrderNumber; if (exists > 0)...
    

    对我来说似乎可行的是:

    string existsQuery = string.format("SELECT 1 from AC_Shipping_Addresses where pk_OrderID = {0}", _Order.OrderNumber); 
    // Or, better yet:
    string existsQuery = "SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = @OrderNumber"; 
    // Now run that query after applying a value to the OrderNumber query param (use code similar to that above); then, if the result is > 0, there is such a record.
    

    【讨论】:

    • 它获取订单 ID (_Order.OrderNumber) 并将其插入 pk_OrderID。有没有一种简单的方法可以在插入之前先检查 _Order.OrderNumber 与可能重复的 pk_OrderID 值吗?
    • 是的,在您要插入的表中检查该值;如果存在,请输入号码。您可能需要继续检查,直到找到一个尚不存在的号码。
    • 要检查该值,这是正确的说法吗? bool alreadyExists = IDAlreadyExists(SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = _Order.OrderNumber, _Order.OrderNumber) > 0;
    • 这应该足够了:“SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = _Order.OrderNumber”
    • 抱歉,我以前从未使用 SQL 查询编写过 if/else 语句。会是这样吗? if ("SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = _Order.OrderNumber" != 0) { //do insert } 如果没有匹配的值,语句会返回 0 还是 NULL?
    【解决方案4】:

    防止插入已经存在的记录。我会检查数据库中是否存在 ID 值。对于使用 IDENTITY PRIMARY KEY 创建的表的示例:

    CREATE TABLE [dbo].[Persons] (    
        ID INT IDENTITY(1,1) PRIMARY KEY,
        LastName VARCHAR(40) NOT NULL,
        FirstName VARCHAR(40)
    );
    

    当数据库中已经存在 JANE DOE 和 JOE BROWN 时。

    SET IDENTITY_INSERT [dbo].[Persons] OFF;
    INSERT INTO [dbo].[Persons] (FirstName,LastName)
    VALUES ('JANE','DOE'); 
    INSERT INTO Persons (FirstName,LastName) 
    VALUES ('JOE','BROWN');
    

    表 [dbo].[Persons] 的数据库输出将为:

    ID    LastName   FirstName
    1     DOE        Jane
    2     BROWN      JOE
    

    我会检查是否应该更新现有记录或插入新记录。如下JAVA示例:

    int NewID = 1;
    boolean IdAlreadyExist = false;
    // Using SQL database connection
    // STEP 1: Set property
    System.setProperty("java.net.preferIPv4Stack", "true");
    // STEP 2: Register JDBC driver
    Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
    // STEP 3: Open a connection
    try (Connection conn1 = DriverManager.getConnection(DB_URL, USER,pwd) {
        conn1.setAutoCommit(true);
        String Select = "select * from Persons where  ID = " + ID;
        Statement st1 = conn1.createStatement();
        ResultSet rs1 = st1.executeQuery(Select);
        // iterate through the java resultset
        while (rs1.next()) {
            int ID = rs1.getInt("ID");
            if (NewID==ID) {
                IdAlreadyExist = true;
            }
    
        }
        conn1.close();
    } catch (SQLException e1) {
        System.out.println(e1);
    }
    if (IdAlreadyExist==false) {
        //Insert new record code here
    } else {
        //Update existing record code here
    }
    

    【讨论】:

      【解决方案5】:

      不是 OP 的答案,但由于这是我在 google 中弹出的第一个问题,我还想补充一点,搜索此问题的用户可能需要重新设置他们的表格,我就是这种情况

      DBCC CHECKIDENT(tablename)
      

      【讨论】:

        【解决方案6】:

        这可能是由多种原因造成的,这在一定程度上取决于您在数据库中设置的内容。

        首先,您可以在表中使用一个 PK,该 PK 也是另一个表的 FK,从而形成 1-1 关系。在这种情况下,您可能需要进行更新而不是插入。如果您真的只能有一个订单的一个地址记录,这可能就是正在发生的事情。

        接下来,您可能会使用某种手动过程来提前确定 id。这些手动过程的问题在于,它们可以创建竞争条件,其中两条记录获取相同的最后一个 id 并将其加一,然后第二个不能插入。

        第三,你查询当它被发送到数据库时可能会创建两条记录。要确定是否是这种情况,运行 Profiler 以准确查看您发送的 SQL 代码以及 ti 是否是 select 而不是 values 子句,然后运行 ​​select 并查看是否由于连接而导致某些记录被复制.在任何情况下,即使您像这样动态创建代码,第一个故障排除步骤始终是运行 Profiler 并查看发送的内容是否是您期望发送的内容。

        【讨论】:

          【解决方案7】:

          确保您的表中没有主键值与查询中的主键 ID 相同的行。

          【讨论】:

            猜你喜欢
            • 2013-05-16
            • 1970-01-01
            • 2014-06-30
            • 1970-01-01
            • 1970-01-01
            • 2020-03-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多