Если у меня есть UPDATE
заявление, которое на самом деле не меняет какие-либо данные (потому что данные уже в обновленном состоянии). Есть ли какой-то выигрыш в производительности, если поставить в WHERE
пункт проверку , чтобы предотвратить обновление?
Например, будет ли разница в скорости выполнения между UPDATE 1 и UPDATE 2 в следующем:
CREATE TABLE MyTable (ID int PRIMARY KEY, Value int);
INSERT INTO MyTable (ID, Value)
VALUES
(1, 1),
(2, 2),
(3, 3);
-- UPDATE 1
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2
AND Value <> 2;
SELECT @@ROWCOUNT;
-- UPDATE 2
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2;
SELECT @@ROWCOUNT;
DROP TABLE MyTable;
Причина, по которой я спрашиваю, состоит в том, что мне нужно, чтобы число строк включало неизмененную строку, поэтому я знаю, нужно ли делать вставку, если идентификатор не существует. В качестве такового я использовал форму ОБНОВЛЕНИЕ 2. Если при использовании формы UPDATE 1 есть выигрыш в производительности, возможно ли получить нужное мне количество строк?
sql-server
query-performance
update
Мартин Браун
источник
источник
Ответы:
Конечно, может быть, так как есть небольшая разница в производительности из-за ОБНОВЛЕНИЯ 1 :
Тем не менее, насколько велика разница, вы должны измерить в своей системе с помощью своей схемы, данных и нагрузки на систему. Существует несколько факторов, влияющих на то, какое влияние оказывает обновление без обновления:
UPDATE TableName SET Field1 = Field1
, запускается триггер обновления, который указывает, что поле было обновлено (если вы проверяете с помощью функций UPDATE () или COLUMNS_UPDATED ), и что поля в таблицахINSERTED
и в обоихDELETED
таблицах имеют одно и то же значение.Кроме того, следующий сводный раздел можно найти в статье Пола Уайта « Влияние необновленных обновлений» (как отметил @spaghettidba в комментарии к своему ответу):
Пожалуйста, имейте в виду (особенно если вы не переходите по ссылке, чтобы увидеть полную статью Пола), следующие два пункта:
Необновляющиеся обновления все еще имеют некоторую активность журнала, показывая, что транзакция начинается и заканчивается. Просто изменение данных не происходит (что все еще является хорошей экономией).
Как я уже говорил выше, вам необходимо протестировать свою систему. Используйте те же исследовательские запросы, которые использует Пол, и посмотрите, получите ли вы те же результаты. Я вижу немного отличающиеся результаты в моей системе, чем то, что показано в статье. Все еще нет грязных страниц, которые нужно написать, но немного больше активности журнала.
Проще говоря, если вы имеете дело только с одной строкой, вы можете сделать следующее:
Для нескольких строк вы можете получить информацию, необходимую для принятия этого решения, используя
OUTPUT
предложение. Собирая точно, какие строки были обновлены, вы можете сузить элементы, чтобы найти разницу между отсутствием обновлений строк, которые не существуют, и не обновлять строки, которые существуют, но не нуждаются в обновлении.Я показываю основную реализацию в следующем ответе:
Как избежать использования запроса Merge при загрузке нескольких данных с использованием параметра xml?
Метод, показанный в этом ответе, не отфильтровывает строки, которые существуют, но не нуждаются в обновлении. Эту часть можно добавить, но сначала вам нужно будет точно указать, где вы получаете свой набор данных, в который вы сливаетесь
MyTable
. Они приходят с временного стола? Табличный параметр (TVP)?ОБНОВЛЕНИЕ 1:
Я наконец смог провести некоторое тестирование, и вот что я нашел относительно журнала транзакций и блокировки. Сначала схема для таблицы:
Затем тест обновляет поле до значения, которое оно уже имеет:
Полученные результаты:
Наконец, тест, отфильтровывающий обновление из-за неизменности значения:
Полученные результаты:
Как видите, при фильтрации строки ничего не записывается в журнал транзакций, в отличие от двух записей, отмечающих начало и конец транзакции. И хотя это правда, что эти две записи почти ничто, они все еще что-то.
Кроме того, блокировка ресурсов PAGE и KEY менее ограничена при фильтрации строк, которые не изменились. Если никакие другие процессы не взаимодействуют с этой таблицей, то это, вероятно, не проблема (но насколько это вероятно, правда?). Имейте в виду, что тестирование, показанное в любом из связанных блогов (и даже мое тестирование) неявно предполагает, что в таблице нет разногласий, поскольку оно никогда не является частью тестов. Сказать, что не обновляющиеся обновления настолько легки, что не нужно платить за фильтрацию, потому что тестирование проводится более или менее в вакууме. Но в Production эта таблица, скорее всего, не является изолированной. Конечно, вполне может быть, что небольшая регистрация и более строгие блокировки не приведут к снижению эффективности. Таким образом, самый надежный источник информации, чтобы ответить на этот вопрос? SQL Server. В частности:ваш SQL Server. Он покажет вам, какой метод лучше для вашей системы :-).
ОБНОВЛЕНИЕ 2:
Если в операциях, в которых новое значение совпадает с текущим значением (т. Е. Без обновления), из числа операций, в которых новое значение отличается, и обновление необходимо, то следующий шаблон может оказаться еще лучше, особенно если на столе много споров. Идея состоит в том, чтобы сначала просто
SELECT
получить текущее значение. Если вы не получите значение, то у вас есть ответ относительноINSERT
. Если у вас есть значение, вы можете сделать простоеIF
и выдатьUPDATE
только, если это необходимо.Полученные результаты:
Таким образом, вместо 3 получено только 2 блокировки, и обе эти блокировки являются Intent Shared, а не Intent eXclusive или Intent Update ( Lock Compatibility ). Помня о том, что каждая полученная блокировка также будет освобождена, каждая блокировка в действительности представляет собой 2 операции, поэтому этот новый метод представляет собой всего 4 операции вместо 6 операций в первоначально предложенном методе. Учитывая, что эта операция выполняется один раз каждые 15 мс (приблизительно, как указано в OP), то есть примерно 66 раз в секунду. Таким образом, первоначальное предложение составляет 396 операций блокировки / разблокировки в секунду, в то время как этот новый метод составляет только 264 операции блокировки / разблокировки в секунду даже для более легких блокировок. Это не гарантия потрясающей производительности, но, безусловно, стоит протестировать :-).
источник
Немного уменьшите масштаб и подумайте о более широкой картине. В реальном мире ваше обновление будет выглядеть так:
Или это будет выглядеть примерно так:
Потому что в реальном мире таблицы имеют много столбцов. Это означает, что вам нужно будет генерировать много сложной динамической логики приложения для построения динамических строк, ИЛИ вам придется каждый раз указывать содержимое до и после каждого поля.
Если вы динамически создаете эти операторы обновления для каждой таблицы, передавая только обновляемые поля, вы можете быстро столкнуться с проблемой загрязнения кэша плана, аналогичной проблеме размеров параметров NHibernate, возникшей несколько лет назад. Еще хуже, если вы создадите операторы обновления в SQL Server (как в хранимых процедурах), то вы будете сжигать драгоценные циклы ЦП, потому что SQL Server не очень эффективен для объединения строк в масштабе.
Из-за этих сложностей обычно не имеет смысла проводить такого рода построчное сравнение по полям, когда вы выполняете обновления. Вместо этого подумайте об операциях на основе множеств.
источник
Вы можете увидеть увеличение производительности при пропуске строк, которые не нужно обновлять только при большом количестве строк (меньше журналирования, меньше грязных страниц для записи на диск).
При работе с обновлениями в одну строку, как в вашем случае, разница в производительности совершенно незначительна. Если обновление строк во всех случаях облегчает вам задачу, сделайте это.
Для получения дополнительной информации по этой теме см. Обновления без обновлений Пола Уайта.
источник
Вы можете объединить обновление и вставить в один оператор. В SQL Server вы можете использовать оператор MERGE для обновления и вставки, если он не найден. Для MySQL вы можете использовать INSERT ON DUPLICATE KEY UPDATE .
источник
Вместо того, чтобы проверять значения всех полей, разве вы не можете получить значение хеш-функции, используя интересующие вас столбцы, а затем сравнить его с хеш-значением, сохраненным для строки в таблице?
источник