То, которое быстрее, СУЩЕСТВУЕТ прежде или после ВСТАВКИ?

У меня есть SP в SQL Server, который выполняет сотни времен в минуту и должен проверить входящий трафик по базе данных. В данный момент это делает следующее

INSERT INTO table
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2);

Однако я мог также пойти с

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)    
   INSERT INTO table (value1,value2) VALUES (@value1,@value2);

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

ОБНОВЛЕНИЕ:Ой... предназначенный, чтобы указать то, что СУЩЕСТВУЕТ, использует больше чем 1 значение, чтобы найти, существует ли запись, таким образом, ограничение на уникальность данных не будет работать. Отредактированный образец для отражения этого...

8
задан roryok 17 February 2010 в 16:57
поделиться

6 ответов

После добавления газиллиона комментариев к этому вопросу и ответам на него, я попробую ответить на него по-своему.

Я бы не ожидал большой разницы в производительности между двумя предложенными в исходном вопросе подходами. С одной стороны, как указал Рэй, второй подход может избавить вас от необходимости делать некоторые приготовления к вставке, но с другой стороны, РСУБД обычно лучше всего работает с пакетными операторами, как в первом решении.

KM и DVK предлагают добавить ограничение UNIQUE, которое сделает проверку уникальности неявной, но потребует от вас добавления некоторой обработки ошибок вокруг оператора INSERT. Мне трудно понять, почему это должно увеличить производительность, предполагая, что у вас уже есть индекс, покрывающий эти два столбца. Если у вас нет такого индекса, добавьте его и пересмотрите потребность в дополнительной производительности.

Явная или неявная проверка уникальности не имеет значения AFAIK. Если что-то и выигрывает от того, что проверка выполняется "внутри" желудка СУБД, то этот выигрыш может быть просто съеден накладными расходами, связанными с возникновением и обработкой ошибок при наличии дубликатов.


Итог: Предполагая, что индекс уже установлен, если вы все еще жаждете производительности, я рекомендую вам провести эмпирические тесты трех предложенных решений. Составьте небольшую программу, имитирующую ожидаемые входные данные, и обработайте каждое из трех решений несколькими миллиардами строк, включая правдоподобное количество дубликатов. сделайте это и не забудьте опубликовать свои результаты :-)

.
1
ответ дан 5 December 2019 в 15:22
поделиться

В среде с ограниченным параллелизмом одновременный INSERT может произойти между IF NOT EXISTS и INSERT во втором запросе.

Ваш первый запрос установит общие блокировки на исследуемую запись, которые не будут сняты до конца запроса, поэтому будет невозможно вставить новую запись, пока запрос не будет запущен.

Однако не следует полагаться исключительно на такое поведение. Установите дополнительное ограничение UNIQUE на значение .

Это не только сделает базу данных более согласованной, но и создаст индекс, который сделает первый запрос более быстрым.

3
ответ дан 5 December 2019 в 15:22
поделиться

Если бы мне пришлось догадаться, я бы предположил, что второй вариант будет быстрее. SQL Server не должен будет выполнять какую-либо настройку для вставки, если существует сбой, в то время как в первом случае он может искать некоторые имена таблиц и полей и подготовиться к вставке, которая никогда не произойдет. Однако я бы попробовал это в анализаторе запросов и посмотрел, что говорит план.

-121--3653357-

С главной страницы:

Отправка почты

Для отправки сообщения одному или нескольким пользователям можно вызвать почтовую рассылку с помощью аргументы, которые являются именами люди, которым будет отправлена почта. Ожидается, что пользователь введет его сообщение, последовало «control-D» в начале строки.

Другими словами, mailx считывает содержимое для отправки из стандартного ввода и может быть перенаправлен в обычный режим. Например:

ls -l $HOME | mailx -s "The content of my home directory" someone@email.adr
-121--796045-

просто сделайте это и проигнорируйте любую ошибку (предполагает уникальное ограничение на значение)...

BEGIN TRY
    INSERT INTO Table (value) VALUES (@value);
END TRY
BEGIN CATCH
    PRINT 'it was already in there!'
END CATCH

Так как это выполняется сотни раз в минуту , подсказки блокировки должны быть добавлены к ПО и транзакции, чтобы избежать условия гонки

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK)  WHERE value = @value);

, однако, моя предложенная идея просто ВСТАВИТЬ и игнорировать любую повторяющуюся ошибку ограничения также позволит избежать условия гонки.

1
ответ дан 5 December 2019 в 15:22
поделиться

Если вы хотите, чтобы значения были уникальными, почему бы просто не создать уникальное ограничение на значение, выполнить INSERT без SELECT и изящно обработать ошибку нарушения ограничения?

Это будет быстрее, чем любой из этих подходов.

Кроме того, ваш первый подход не работает - к тому времени, как вы доберетесь до select, вы уже вставили значение, поэтому select, очевидно, найдет то, что вы только что вставили.

1
ответ дан 5 December 2019 в 15:22
поделиться

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

0
ответ дан 5 December 2019 в 15:22
поделиться

Оба варианта неверны. Вы вставите пары повторяющихся @ value1, @ value2, гарантированно .

Правильный способ справиться с этим - наложить уникальное ограничение на два столбца и всегда ВСТАВИТЬ и обработать нарушение ограничения:

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2);

и вставить:

BEGIN TRY
   INSERT INTO Table (value1, value2) VALUES (@value1, @value2);
END TRY
BEGIN CATCH
   DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT;
   SET @error_number = ERROR_NUMBER();
   SET @error_message = ERROR_MESSAGE();
   SET @xact_state = XACT_STATE();
   IF (@xact_state = -1)
   BEGIN
     ROLLBACK TRANSACTION;
   END
   IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */
   BEGIN
      RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message);
   END
ENd CATCH

Хотя это может показаться сложным, необходимо учесть небольшую деталь, названную корректностью . Это намного проще по сравнению с решением на основе подсказок блокировки. Это также наиболее эффективное решение: выполняет только один поиск. Для всех остальных решений требуется как минимум два поиска (один для проверки возможности вставки, второй для вставки).

5
ответ дан 5 December 2019 в 15:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: