Нарушение ограничения первичного ключа. Не удается вставить дубликат ключа в объект-SQL Server

я унаследовал проект, и я столкнулся с 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'. Значение повторяющегося ключа is (165863).

из чтения подобных вопросов кажется, что я должен объявить идентификатор в заявлении.

это правильно? Как настроить код, чтобы устранить эту проблему?

любая помощь очень ценится!

5 ответов


довольно уверен, что pk_OrderID является ПК AC_Shipping_Addresses

и вы пытаетесь вставить дубликат через _Order.Номер заказа

сделать

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) 

Я получал ту же ошибку в восстановленной базе данных, когда пытался вставить новую запись с помощью EntityFramework. Оказалось, что индентичность / семя все портит.

С помощью команды reseed исправлено.

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

какое значение вы передаете первичному ключу (предположительно "pk_OrderID")? Вы можете настроить его на автоматическое приращение, и тогда никогда не должно быть проблемы с дублированием значения - БД позаботится об этом. Если вам нужно указать значение самостоятельно, вам нужно написать код, чтобы определить максимальное значение для этого поля, а затем увеличить его.

если у вас есть столбец с именем "ID" или такой, который не отображается в запросе, это нормально, если он настроен на autoincrement-но это, вероятно, нет, или вы не должны получить эту ошибку msg. Кроме того, вам было бы лучше написать более простой на глаз запрос и использовать 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", и значение-это идентификатор, который вы потенциально собираетесь вставить:

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 разных способа вставки записей без наличия ошибки:

1) когда IDENTITY_INSERT установлен. Первичный ключ " ID " НЕ ДОЛЖЕН ПРИСУТСТВОВАТЬ

2)Когда IDENTITY_INSERT установлен на. Первичный ключ " ID "ДОЛЖЕН ПРИСУТСТВОВАТЬ И ЗНАЧЕНИЕ" ID " НЕ ДОЛЖНО СУЩЕСТВОВАТЬ В БАЗЕ ДАННЫХ

в следующем примере из той же таблицы, созданной с первичным идентификатором Ключ:

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

1) в первом примере вы можете вставить новые записи в таблицу без получения ошибки, когда IDENTITY_INSERT выключен. Первичный ключ " ID " НЕ ДОЛЖЕН ПРИСУТСТВОВАТЬ из операторов "INSERT INTO"и уникальное значение ID будет добавлено автоматически:. Если идентификатор присутствует из вставки в этом случае, вы получите ошибку " не удается вставить явное значение для столбца identify в таблице..."

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

база данных Вывод таблицы [dbo].[Лица] будут:

ID    LastName   FirstName
1     DOE        Jane
2     BROWN      JOE

2) во втором примере вы можете вставлять новые записи в таблицу без получения ошибки, когда IDENTITY_INSERT включен. Первичный ключ " ID " ДОЛЖЕН ПРИСУТСТВОВАТЬ из операторов" INSERT INTO " и**ЗНАЧЕНИЕ ПЕРВИЧНОГО КЛЮЧА НЕ ДОЛЖНО УЖЕ СУЩЕСТВОВАТЬ: если идентификатор отсутствует из вставки в этом случае, вы получите ошибку " явное значение должно быть указано для столбца идентификатора таблица...". Если значение первичного ключа уже существует в базе данных, вы получите следующую ошибку "нарушение ограничения первичного ключа ... Не удается вставить дубликат ключа в объект"

SET IDENTITY_INSERT [dbo].[Persons] ON;
INSERT INTO [dbo].[Persons] (ID,FirstName,LastName)
VALUES (5,'JOHN','WHITE'); 
INSERT INTO [dbo].[Persons] (ID,FirstName,LastName)
VALUES (3,'JACK','BLACK'); 

вывод базы данных таблицы [dbo].[Лица] будут:

ID    LastName   FirstName
1     DOE        Jane
2     BROWN      JOE
3     BLACK      JACK
5     WHITE      JOHN

может быть несколько вещей, вызывающих это, и это несколько зависит от того, что вы настроили в своей базе данных.

во-первых, вы можете использовать PK в таблице, которая также является FK для другой таблицы, делая отношение 1-1. В этом случае может потребоваться обновление, а не вставка. Если вы действительно можете иметь только одну запись адреса для заказа, это может быть то, что происходит.

Далее вы можете использовать какой-то ручной процесс для определения идентификатора впереди время. Проблема с этими ручными процессами заключается в том, что они могут создавать условия гонки, когда две записи набирают один и тот же последний идентификатор и увеличивают его на один, а затем второй может вставить.

В-третьих, запрос по мере его отправки в базу данных может создавать две записи. Чтобы определить, так ли это, запустите Profiler, чтобы увидеть, какой именно код SQL вы отправляете, и если ti является select вместо предложения values, затем запустите select и посмотрите, есть ли у вас из-за соединений, полученных некоторые записи должны быть продублированы. В любом случае, даже когда вы создаете код на лету, как это первый шаг по устранению неполадок всегда запустить Profiler и посмотреть, если то, что было отправлено, было то, что вы ожидали быть отправлены.