Является ли SQL IN плохой для производительности? - программирование
Подтвердить что ты не робот

Является ли SQL IN плохой для производительности?

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

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 - это ключи в другой таблице. Это приложение для форума, вкратце объясняющее: С# получает все форумы из базы данных и сохраняет их в кэше приложений. Прежде чем С# вызовет процедуру, которая получает потоки для этих форумов и для этого пользователя, С# делает некоторую логическую фильтрацию коллекции "все форумы", учитывая разрешения и некоторую бизнес-логику. Тайм-аут происходит в базе данных, а не в самом приложении. Выполнение всей этой логики в запросе потребует много внутренних объединений, и я не уверен на 100%, что я могу сделать все это внутри процедуры.

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

Ответ 3

Вы можете попытаться создать временную таблицу, вставить в нее свои значения и использовать таблицу в предикате 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, но улучшить производительность запросов.

Update:

Из вашего плана выполнения я вижу, что вам нужен составной индекс на (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, все в порядке для этого самого запроса, но старайтесь избегать его вообще.

Ответ 4

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

Начните с плана запроса для запроса, который фактически отключается. Знаете ли вы, какой именно запрос?

Ответ 5

В зависимости от вашего распределения данных дополнительные предикаты в вашем предложении WHERE могут повысить производительность. Например, если набор идентификаторов мал относительно общего числа в таблице, и вы знаете, что идентификаторы относительно близки друг к другу (возможно, они обычно будут последними дополнениями и поэтому кластеризованы в верхнем конце диапазона) вы можете попытаться включить предикат "AND FieldW BETWEEN 109 AND 891" (после определения идентификатора min и max в вашем наборе кода С#). Возможно, выполнение сканирования диапазона по этим столбцам (если индексируется) работает быстрее, чем то, что в настоящее время используется.

Ответ 6

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

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

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

Ответ 10

Как правило, я бы использовал пользовательский тип таблицы для таких запросов.

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

Ответ 11

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

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

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

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

Ответ 12

Производительность может оцениваться только в контексте того, что вы пытаетесь сделать. В этом случае вы запрашиваете извлечение около 70 строк (при условии, что thay являются уникальными значениями), поэтому вы можете ожидать чего-то вроде 70-кратной продолжительности получения одного значения. Это может быть меньше из-за кеширования или курса.

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

Ответ 13

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

В вашем случае: это выглядит не так уж плохо.

Ответ 14

Вы можете попробовать что-то вроде:

select a.FieldX, a.FieldY
from (
    select FieldW = 108 union
    select FieldW = 109 union
    select FieldW = 113 union
    ...
    select FieldW = 891
) _a
join A a on a.FieldW = _a.FieldW

Это может быть уместно для вашей ситуации, например, когда вы хотите генерировать одну инструкцию SQL динамически. На моей машине (SQL Server 2008 Express), тестируя небольшое число (5) значений FieldW и большое количество (100 000) строк в A, это использует поиск индекса на A с вложенными петлями, соединяющими между A и _a, вероятно, это то, что вы ищете.