Предположим, что структура таблицы MyTable(KEY, datafield1, datafield2...)
.
Часто я хочу либо обновить существующую запись, либо вставить новую запись, если она не существует.
По существу:
IF (key exists)
run update command
ELSE
run insert command
Как лучше всего написать это?
не забывайте о транзакциях. Производительность хороша, но проста (ЕСЛИ СУЩЕСТВУЕТ..) подход очень опасен.
, Когда несколько потоков попытаются работать Вставлять-или-обновлять, можно легко получить нарушение первичного ключа.
Решения, предоставленные @Beau Crawford & @Esteban показывают общее представление, но подверженный ошибкам.
Для предотвращения мертвых блокировок и нарушений PK можно использовать что-то вроде этого:
begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
update table set ...
where key = @key
end
else
begin
insert into table (key, ...)
values (@key, ...)
end
commit tran
или
begin tran
update table with (serializable) set ...
where key = @key
if @@rowcount = 0
begin
insert into table (key, ...) values (@key,..)
end
commit tran
Сделайте UPSERT:
UPDATE MyTable SET FieldA=@FieldA WHERE Key=@Key IF @@ROWCOUNT = 0 INSERT INTO MyTable (FieldA) VALUES (@FieldA)
Если Вы хотите к UPSERT больше чем одну запись за один раз, можно использовать СЛИЯНИЕ оператора ANSI SQL:2003 DML.
MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])
Выполнение, если... еще существует..., включает выполнение двух минимумов запросов (один для проверки, один для принятия мер). Следующий подход требует только одного, где запись существует, два, если вставка требуется:
DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')
Я обычно делаю то, что несколько из других плакатов сказали относительно проверки его существующий первый и затем делающий независимо от того, что корректный путь. Одна вещь, которую необходимо помнить при выполнении этого, состоит в том, что план выполнения, кэшируемый sql, мог быть неоптимальным для одного пути или другого. Я верю лучшему способу сделать, это должно назвать две различных хранимых процедуры.
FirstSP: If Exists Call SecondSP (UpdateProc) Else Call ThirdSP (InsertProc)
Теперь, я не следую своему собственному совету очень часто, поэтому беру его с мелкой частицей соли.
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)
Редактирование:
увы, даже к моему собственному вреду, я должен допустить решения, которые делают это без выбора, кажется, лучше, так как они выполняют задачу с одним меньшим количеством шага.
При использовании ADO.NET DataAdapter обрабатывает это.
, Если Вы хотите обработать его сами, это - путь:
Удостоверяются, что существует ограничение первичного ключа на Ваш столбец ключа.
Тогда Вы:
можно также сделать это наоборот, т.е. сделать вставку сначала и сделать обновление, если вставка перестала работать. Обычно первый путь лучше, потому что обновления делаются чаще, чем вставляет.
Сделайте выбор, если Вы получаете результат, обновляете его, в противном случае создаете его.
SQL Server MS 2008 представляет оператор MERGE, которому я верю, является частью стандарта SQL:2003. Поскольку многие показали, это не грандиозное предприятие обработать случаи строки, но при контакте с большими наборами данных, каждому нужен курсор со всеми проблемами производительности, которые приходят. Оператор MERGE будет очень одобренным дополнением при контакте с большими наборами данных.
Посмотрите мой подробный ответ на очень похожий предыдущий вопрос
, @Beau Crawford является хорошим путем в SQL 2005 и ниже, хотя, если Вы предоставляете представителю, это должно перейти в первый парень к ТАК этому . Единственная проблема состоит в том, что для вставок это - все еще две операции IO.
мс Sql2008 представляет merge
из стандарта SQL:2003:
merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
as source (field1, field2)
on target.idfield = 7
when matched then
update
set field1 = source.field1,
field2 = source.field2,
...
when not matched then
insert ( idfield, field1, field2, ... )
values ( 7, source.field1, source.field2, ... )
Теперь это - действительно всего одна операция IO, но ужасный код:-(
format = format.withLocale(Locale.US);
иначе перестанет работать парсинг January
.//Joda-новичок
– Kennet
19.12.2019, 01:12
HOLDLOCK
для операций слияния в высоких ситуациях с параллелизмом.
– Keith
19.05.2020, 15:28
upsert
, что примерно все другие поставщики БД решили поддерживать вместо этого. upsert
синтаксис является намного более хорошим способом сделать это, таким образом, по крайней мере MS должен был поддерживать его также - it' s не как it' s единственное нестандартное ключевое слово в T-SQL
– Keith
19.05.2020, 15:29
MERGE
синтаксис.
– Seph
19.05.2020, 15:29