Нарушение ограничения первичного ключа. Не удается вставить дубликат ключа в объект-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 и посмотреть, если то, что было отправлено, было то, что вы ожидали быть отправлены.