违反PRIMARY KEY约束。无法在对象中插入重复键 - SQL Server

时间:2016-03-16 17:59:06

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

我继承了一个项目,但我遇到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错误:

  

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

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

这是对的吗?我如何调整代码来解决这个问题?

非常感谢任何帮助!

7 个答案:

答案 0 :(得分:9)

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

使用重新设定命令修复它。

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

答案 1 :(得分:3)

非常确定pk_OrderID是AC_Shipping_Addresses的PK

您正试图通过_Order.OrderNumber

插入副本

做一个

select * from AC_Shipping_Addresses where pk_OrderID = 165863;

或选择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) 

答案 2 :(得分:2)

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

如果你有一个名为“ID”的列或者查询中没有显示的列,只要设置为自动增量就可以了 - 但它可能不是,或者你不应该得到那个错误的消息。此外,您最好还是编写一个更容易上手的查询并使用params。因为九年后的推论,如果您只是填写用户输入的值,那么您将使数据库对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

Justin想知道这是否有效:

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.

答案 3 :(得分:1)

防止插入已存在的记录。我检查数据库中是否存在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');

TABLE [dbo]的数据库输出。[人员]将是:

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
}

答案 4 :(得分:0)

可能有几件事情导致这种情况,这在某种程度上取决于您在数据库中设置的内容。

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

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

第三,你查询因为它被发送到数据库可能会创建两个记录。要确定是否是这种情况,请运行Profiler以准确查看要发送的SQL代码,如果ti是select而不是values子句,则运行select并查看是否由于连接而导致某些记录被复制。即使在你动态创建代码时,第一个故障排除步骤仍然是运行Profiler,看看发送的是你期望发送的内容。

答案 5 :(得分:0)

不是OP的答案,但由于这是在Google中为我弹出的第一个问题,因此Id还想补充一点,搜索此内容的用户可能需要重新设置表格,对我而言就是这样

DBCC CHECKIDENT(tablename)

答案 6 :(得分:0)

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