2015-10-19 5 views
13

В последнее время я делаю все возможное, чтобы найти лучший способ запуска определенных запросов в SQL, который можно было бы сделать несколькими различными способами. Среди моих исследований я встретил довольно много ненависти к концепции WHERE IN из-за присущей неэффективности в том, как она работает.SQL UPDATE WHERE IN (Список) или UPDATE каждый отдельно?

например: WHERE Col IN (val1, val2, val3)

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

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (id1, id2, id3 ....); 

В приведенном выше списке идентификаторов может быть до 1.5k ID.

VS

Перебор всех идентификаторов в коде, и выполнив следующую инструкцию для каждого:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID = 'theID'; 

к себе, это кажется более логичным, что первый будет работать лучше/быстрее, потому что там меньше запросы для запуска. Тем не менее, я не на 100% знаком с in и out of SQL и как работает очередь запросов.

Я также не уверен относительно того, что было бы более дружелюбным в БД до блокировки стола и других общих характеристик.

Общая информация, если это помогает, я использую Microsoft SQL Server 2014, а основным языком разработки является C#.

Любая помощь очень ценится.

РЕДАКТИРОВАТЬ:

Вариант 3:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

В приведенном выше описании, @definedTable является SQL 'User Defined Тип стола', где данные внутри приходит через к хранимой процедуре, как (в C#) типа SqlDbType.Structured

Люди спрашивают, как идентификаторы бывают: идентификаторы находятся в List<string> в коде, и используются для других вещей в коде до этого направляется в хранимую процедуру. В настоящее время идентификаторы входят в хранимую процедуру как «Пользовательский тип таблицы» с одним столбцом (идентификаторы).

Я думал, имея их в таблице может быть лучше, чем иметь код конкатенации массивную строку и просто плюя его в СП в качестве переменной, которая выглядит как id1, id2, id3, id4 и т.д.

+1

Вы пытались просмотреть план выполнения, если это быстрее из двух запросов? – Japongskie

+1

Откуда id1, id2, id3? В большинстве практических случаев они поступают из другой таблицы, в результате фильтрации при некоторых условиях. В этом случае вам лучше присоединиться к этому столу, чтобы получить достойную производительность. –

+1

Как вы передаете эти идентификаторы в SQL? У вас есть список значений в коде C# или вы получаете их в результате другого SQL-запроса? – DavidG

ответ

5

Я использую свой третий вариант, и он прекрасно работает.

В моей хранимой процедуре имеется table-valued parameter. См. Также Use Table-Valued Parameters.

В процедуре есть один оператор, нет петель, как вы сказали:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

Лучше вызвать процедуру один раз, чем 1500 раз. Лучше иметь одну транзакцию, чем 1500 транзакций.

Если количество строк в @definedTable выше, скажем, 10K, я бы подумал о его разбиении партиями 10K.


Ваш первый вариант OK для нескольких значений в предложении IN, но когда вы получаете на самом деле высокие цифры (60K +) вы можете увидеть что-то вроде этого, как показано на this answer:

Msg 8623, уровень 16, состояние 1, строка 1 Обработчик запросов закончился из внутренних ресурсов и не смог создать план запроса. Это редкое событие и ожидается только для чрезвычайно сложных запросов или запросов, которые ссылаются на очень большое количество таблиц или разделов. Пожалуйста, упростите запрос . Если вы считаете, что получили это сообщение по ошибке, обратитесь в Службу поддержки клиентов за дополнительной информацией.

1

Вы должны определенно не использовать петлю и отправить полный новый оператор SQL для каждого идентификатора. В этом случае SQL-движок должен перекомпилировать инструкцию SQL и составить план выполнения и т. Д. Каждый раз.

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

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

EDIT:

Perl псевдокод, как описано ниже:

#!/usr/bin/perl 
use DBI; 
$dbh = DBI->connect('dbi:Oracle:MY_DB', 'scott', 'tiger', { RaiseError => 1, PrintError =>1, AutoCommit => 0 }); 
$sth = $dbh->prepare ("UPDATE table1 SET somecolumn = ? WHERE id = ?"); 
foreach $tuple (@updatetuples) { 
    $sth->execute($$tuple[1], $$tuple[0]); 
} 
$dbh->commit; 
$sth->finish; 
$dbh->disconnect; 
exit (0); 
+0

Я думал, что данные поступают из вашей программы, а не другой стол. Это не совсем понятно. Но в этом случае я все еще думаю, что это лучший маршрут. Я не знаю C#, так что вот несколько непроверенных псевдокодов Perl: О, я думаю, я должен ответить на этот вопрос. – Laserbeak

2

Ваши первые или третьи варианты - лучший способ пойти. Для любого из них вам нужен индекс на table1(id).

В общем случае лучше запускать один запрос, а не несколько запросов, поскольку накладные расходы на передачу данных из базы данных и из нее складываются. Кроме того, каждое обновление запускает транзакции и фиксирует это - больше накладных расходов. Тем не менее, это, вероятно, не будет важно, если вы не обновляете тысячи записей. Накладные расходы измеряются сотнями микросекунд или миллисекунд в типичной системе.