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

问题描述 投票:0回答:8

在电子商务网站上,此代码将订单运输信息插入另一个数据库:

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 错误:

Violation of PRIMARY KEY constraint 'PK_AC_Shipping_Addresses'. Cannot insert duplicate key in object 'dbo.AC_Shipping_Addresses'. The duplicate key value is (165863).

我需要在声明中申报ID吗?

我该如何调整代码来解决这个问题?

sql-server sql-server-2008 primary-key
8个回答
32
投票
当我尝试使用 EntityFramework 插入新记录时,我在恢复的数据库上遇到了相同的错误。事实证明,身份/种子把事情搞砸了。

使用重新设定种子命令修复了该问题。

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

4
投票
我很确定

pk_OrderID

AC_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)
    

2
投票
您传递给主键的值是什么(大概是“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.
    

2
投票
不是OP的答案,但由于这是我在谷歌中出现的第一个问题,我还想补充一点,搜索此问题的用户可能需要重新播种他们的表,这对我来说就是这种情况

DBCC CHECKIDENT(tablename)
    

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');

表 [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 }
    

1
投票
我需要将架构和数据从本地内部 SQL 服务器移动到 Azure SQL 服务器。

为了实现这个目标我

    尝试从内部服务器到 azure 的 bacpac。
  1. 使用了 SSMS 的功能之一,例如任务 > 将数据库部署到 Microsoft Azure SQL 数据库。
  2. 使用了微软数据迁移助手工具。
问题是,当脚本执行时,不知何故,面临这个已经有 id 问题的表自动添加了记录,这可能是因为光标或应用了任何唯一键。

所以在执行这个查询之前你必须检查是否有记录。


0
投票
可能有多种原因导致此问题,这在一定程度上取决于您在数据库中设置的内容。

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

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

第三,您的查询在发送到数据库时可能会创建两条记录。要确定是否是这种情况,请运行 Profiler 以查看您正在发送的 SQL 代码,以及 ti 是否是 select 而不是值子句,然后运行 select 并查看是否由于连接而获得了一些要重复的记录。即使您像这样动态创建代码,第一个故障排除步骤始终是运行 Profiler 并查看发送的内容是否是您期望发送的内容。


0
投票
确保您的表中是否还没有主键值与查询中的主键 ID 相同的行。

© www.soinside.com 2019 - 2024. All rights reserved.