Короткая версия
Опубликованные запросы, с другой стороны, имеют серьезные недостатки и могут привести к получению неверных данных.Вы можете вставить все строки в один безопасный оператор, который не требует чрезмерных блокировок:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, [delete])
SELECT
newrows.key1, newrows.key2, newrows.name, newrows.value,
newrows.timestamp, newrows.changed, newrows.disabled, newrows.[delete]
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
LEFT OUTER JOIN AppVersion
ON newrows.key1=AAppversion.key1 and newrows.key2=Appversion.key2
WHERE Appversion.key1 is null and #Appversion.key2 is null
Объяснение
Во-первых, в этих запросах возникает несколько серьезных проблем.Подсказка NOLOCK
и попытка использовать DELETE
вместо транзакций являются очень убедительным признаком того, что существуют проблемы с блокировкой.Попытка прикрыть их, хотя и не поможет, на самом деле это делает вещи хуже .Например, NOLOCK
не означает don't take locks
.Это означает don't respect locks, ie read dirty data while taking excessive locks yourself
.Это означает, что если какая-то другая транзакция вставляет те же данные и удаляет их, ваш запрос все равно может их видеть.
Я подозреваю, что отсутствуют индексы или неправильно написанные запросы, которые приводят к блокировке.Возможно, длительные транзакции, которые в конечном итоге блокируют друг друга?Помните, что SQL Server является одной из самых быстрых баз данных.Он может обрабатывать миллионы транзакций в час на террабайтах данных без таких ухищрений.Эти проблемы должны быть исправлены, а не скрыты.Например, отсутствующий индекс означает, что запросу SELECT может потребоваться заблокировать lot строк при попытке найти те, которые соответствуют его критериям.При правильном индексе ему, вероятно, придется блокировать только 1 строку.
INSERT .. SELECT
используется для вставки результатов запросов, а не конкретных значений.Чтобы вставить конкретные значения, вы должны использовать предложение VALUES
.Вы можете добавить несколько строк в одном операторе, например:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
Отдельные операторы INSERT, DELETE, UPDATE являются атомарными, т.е. они будут автоматически откатываться в случае неудачи.Они не не нуждаются в явной транзакции.В любом случае удаление строк равно , а не так же, как откат.Если есть серьезная проблема, предложение DELETE
может никогда не вызываться, оставляя записи-призраки в базе данных.
Если вы хотите вставить только новые записи, вы можете слева соединить значения с целевой таблицей и вставить только новые.Если вы сравнивали исходную и целевую таблицы, вы могли бы написать:
INSERT into Target (ID,a,b,c)
SELECT source.ID,source.a,source.b,source.c
FROM source
LEFT OUTER JOIN target on source.ID = target.ID
where target.ID is null
При этом будут выбраны только исходные строки, которые не имеют соответствующего целевого ключа, и вставлены их.
Значения можно рассматривать какесли они тоже были таблицей, заключив их в круглые скобки и указав имена таблиц и столбцов, например:
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
Таким образом, вы можете вставить только новые строки с:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, [delete])
SELECT
newrows.key1, newrows.key2, newrows.name, newrows.value,
newrows.timestamp, newrows.changed, newrows.disabled, newrows.[delete]
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
LEFT OUTER JOIN AppVersion
ON newrows.key1=AAppversion.key1 and newrows.key2=Appversion.key2
WHERE Appversion.key1 is null and #Appversion.key2 is null
Уменьшение блокировки
Что касается проблем с подразумеваемой блокировкой, вам следует исключить длительные транзакции и ненужные запросы, удалить NOLOCK и убедиться, что все таблицы имеют правильные индексы.Если AppVersion
имеет первичный ключ, который начинается со столбцов key1
и key2
, или если существует индекс, начинающийся с них, блокировка будет минимальной.
Другим соображением является использование Изоляция снимков , особенно READ_COMMITTED_SNAPSHOT
на уровне базы данных.Это позволит читателям читать существующие данные, даже если автор начал их изменять.Сервер начнет делать копии старых строк, когда авторы изменяют их, гарантируя, что читатели все еще могут читать данные, не блокируя авторов.
В некотором смысле это то, что вы пытались (но не смогли) сделать с подсказкой NOLOCK
.