SQL IN плохо влияет на производительность? - PullRequest
57 голосов
/ 18 июня 2009

У меня есть запрос, выполняющий что-то вроде:

SELECT FieldX, FieldY FROM A
WHERE FieldW IN (108, 109, 113, 138, 146, 160,
307, 314, 370, 371, 441, 454 ,457, 458, 479, 480,
485, 488, 490, 492, 519, 523, 525, 534, 539, 543,
546, 547, 550, 564, 573, 629, 642, 643, 649, 650,
651, 694, 698, 699, 761, 762, 768, 772, 773, 774,
775, 778, 784, 843, 844, 848, 851, 852, 853, 854,
855, 856, 857, 858, 859, 860, 861, 862, 863, 864,
865, 868, 869, 871, 872, 873, 891) 

Имеет предложение IN с таким количеством опций, плохо ли это для производительности запросов? Я испытываю много тайм-аутов в своем приложении, и я считаю, что это может быть источником такой проблемы. Можно ли оптимизировать запрос, не удаляя цифры, используя любую хорошую подсказку SQL?

EDIT:

@ KM это ключи в другой таблице. Это приложение для форума, кратко объясняющее: c # получает все форумы из базы данных и сохраняет их в кеше приложения. Прежде чем C # вызывает процедуру, которая получает потоки для этих форумов и для этого пользователя, c # выполняет некоторую логику, фильтруя коллекцию "все форумы", учитывая разрешения и некоторую бизнес-логику. Тайм-аут происходит в базе данных, а не в самом приложении. Выполнение всей этой логики в запросе потребует большого количества внутренних соединений, и я не уверен на 100%, что смогу сделать все это внутри процедуры.

Я использую SQL Server 2000

Ответы [ 14 ]

111 голосов
/ 18 июня 2009

При написании запроса с использованием оператора IN есть несколько соображений, которые могут повлиять на производительность.

Во-первых, предложения IN обычно внутренне переписываются большинством баз данных для использования логической связки OR. Так col IN ('a','b','c') переписывается в: (COL = 'a') OR (COL = 'b') or (COL = 'c'). План выполнения для обоих запросов будет вероятным эквивалентным при условии, что у вас есть индекс для col.

Во-вторых, когда вы используете IN или OR с переменным числом аргументов, вы заставляете базу данных пересматривать запрос и перестраивать план выполнения при каждом изменении аргументов. Построение План выполнения запроса может быть дорогостоящим шагом. Большинство баз данных кэшируют планы выполнения запросов, которые они выполняют, используя текст запроса EXACT в качестве ключа. Если вы выполняете аналогичный запрос, но с другими значениями аргументов в предикате - вы, скорее всего, заставите базу данных потратить значительное количество времени на анализ и построение планов выполнения. Вот почему переменные связывания настоятельно рекомендуется как способ обеспечения оптимальной производительности запросов.

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

В-четвертых, запросы, содержащие IN и OR в предикате, не всегда могут быть оптимально переписаны в параллельной среде. Существуют различные случаи, когда оптимизация параллельного сервера не применяется - MSDN имеет приличный введение в оптимизацию запросов для параллелизма. Тем не менее, как правило, запросы, использующие оператор UNION ALL, тривиально распараллеливаются в большинстве баз данных и, по возможности, предпочтительнее логических связок (таких как OR и IN).

5 голосов
/ 18 июня 2009

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

AFAIK, SQL Server 2000 не может построить хеш-таблицу из набора констант, что лишает оптимизатор возможности использовать HASH SEMI JOIN.

Это поможет, только если у вас нет индекса на FieldW (который у вас должен быть).

Вы также можете попытаться включить столбцы FieldX и FieldY в индекс:

CREATE INDEX ix_a_wxy ON a (FieldW, FieldX, FieldY)

чтобы запрос можно было обслуживать только с помощью индекса.

SQL Server 2000 не хватает опции INCLUDE для CREATE INDEX, и это может немного снизить производительность DML, но повысить производительность запросов.

Обновление:

Из вашего плана выполнения я вижу, что вам нужен составной индекс для (SettingsID, SectionID)

SQL Server 2000 действительно может построить хеш-таблицу из списка констант (и делает это), но Hash Semi Join, скорее всего, будет менее эффективным, чем Nested Loop для запроса.

И только примечание: если вам нужно знать количество строк, удовлетворяющих условию WHERE, не используйте COUNT(column), вместо этого используйте COUNT(*).

A COUNT(column) не считает строки, для которых значение column равно NULL.

Это означает, что, во-первых, вы можете получить результаты, которые вы не ожидали, и, во-вторых, оптимизатору понадобится сделать дополнительные Key Lookup / Bookmark Lookup, если ваш столбец не охватывается индексом, который служит WHERE условие.

Поскольку ThreadId выглядит как CLUSTERED PRIMARY KEY, для этого самого запроса все в порядке, но старайтесь его вообще избегать.

5 голосов
/ 18 июня 2009

Если у вас хороший индекс для FieldW, использовать этот IN совершенно правильно.

Я только что протестировал, и SQL 2000 выполняет сканирование кластерного индекса при использовании IN.

3 голосов
/ 18 июня 2009

В зависимости от распределения данных дополнительные предикаты в предложении WHERE могут повысить производительность. Например, если набор идентификаторов мал по сравнению с общим числом в таблице, и вы знаете, что идентификаторы относительно близко друг к другу (возможно, они обычно будут недавними добавлениями и, следовательно, кластеризованы в верхнем конце диапазона) Вы можете попытаться включить предикат «И FieldW МЕЖДУ 109 И 891» (после определения минимального и максимального идентификатора в вашем наборе в коде C #). Возможно, сканирование диапазона этих столбцов (если они проиндексированы) работает быстрее, чем то, что используется в настоящее время.

3 голосов
/ 18 июня 2009

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

Давайте начнем с плана запроса для запроса, который фактически истекает. Вы точно знаете, какой это запрос?

2 голосов
/ 18 июня 2009

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

1 голос
/ 09 декабря 2015

Я бы обычно использовал определяемый пользователем тип таблицы для таких запросов.

CREATE TYPE [dbo].[udt_int] AS TABLE (
    [id] [int] NOT NULL
)

Используя переменную таблицы и заполняя ее строками для каждого из ваших чисел, вы можете сделать:

SELECT 
    FieldX, 
    FieldY
FROM A
INNER JOIN @myIds B ON
    A.FieldW = B.id
1 голос
/ 18 июня 2009

Вот ваш ответ ...

http://www.4guysfromrolla.com/webtech/031004-1.shtml

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

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

1 голос
/ 18 июня 2009

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

1 голос
/ 18 июня 2009

В основном то, что делает предложение where, это "FieldW = 108 ИЛИ FieldW = 109 ИЛИ FieldW = 113 ...". Иногда вы можете повысить производительность, выполнив несколько операций выбора и комбинируя их с объединением. Например:

SELECT FieldX, FieldY FROM A WHERE FieldW = 108
UNION ALL
SELECT FieldX, FieldY FROM A WHERE FieldW = 109

Но, конечно, это нецелесообразно, когда вы сравниваете столько значений.

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

...