SQL плохо сказывается на производительности?

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

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?

изменить:

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

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

14 ответов


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

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

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

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

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


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

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


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

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

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

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

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

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

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

обновление:

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

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

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

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

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

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


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


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

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


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


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


в основном то, что делает предложение 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 к этой временной таблице.


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


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

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

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

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


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

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

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

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


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

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


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

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, что, вероятно, то, что вы ищете.