Как избежать SQL не IN с помощью проверки замены и длины - PullRequest
2 голосов
/ 21 сентября 2010

У меня есть ситуация, когда мне нужно динамически создавать строки SQL, и я пытаюсь использовать параметры и sp_executesql, где это возможно, чтобы я мог повторно использовать планы запросов. При большом количестве чтения в Интернете и личного опыта я обнаружил, что «NOT IN» и «INNER / LEFT JOIN» являются медленными и дорогими, когда базовая (крайняя левая) таблица большая (1,5 млн. Строк с примерно 50 столбцами). ). Я также читал, что следует избегать использования любых типов функций, поскольку это замедляет запросы, поэтому мне интересно, что хуже?

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

LEN(@param1) = LEN(REPLACE(@param1, [col], '')) 

вместо:

[col] NOT IN('ABD', 'RDF', 'TRM', 'HYP', 'UOE') 

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

В этом примере я могу использовать «=» для NOT IN, и я буду использовать традиционную технику списка для моего IN, или! =, Если это быстрее, хотя я сомневаюсь в этом. Это быстрее, чем использование NOT IN?

В качестве возможной третьей альтернативы, что если бы я знал все другие возможности (возможности IN, которые потенциально могли бы быть в 80-95 раз длиннее списка) и пропустил бы их вместо этого; это будет сделано на бизнес-уровне приложения, чтобы снять нагрузку с SQL Server. Не очень хорошая возможность для повторного использования плана запроса, но если он экономит секунду или две от большого неприятного запроса, то, черт возьми, нет.

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

Мысли

Заранее спасибо за любую помощь / советы / и т. Д.

Ответы [ 3 ]

2 голосов
/ 21 сентября 2010

Как часто (ошибочно) цитируют Дональда Кнута, «преждевременная оптимизация - корень всего зла».
Итак, прежде всего, уверены ли вы, что если вы напишите свой код наиболее понятным и простым способом (чтобы и писать и читать) он работает медленно?Если нет, проверьте его, прежде чем начинать использовать какие-либо «умные» приемы оптимизации.

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

Например, я серьезно сомневаюсь, что ваш запрос с LEN и REPLACE имеет лучшую производительность, чем NOT IN -в любом случае все строки будут отсканированы и проверены на совпадение.Для достаточно длинного списка оптимизатор MSSQL автоматически создаст временную таблицу для оптимизации сравнения на равенство.
Более того, подобные трюки приводят к ошибкам: скажем, ваш пример будет работать неправильно, если [col] = 'AB'.

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

Говоря о передаче списка переменной длины на сервер, здесь много дискуссий о SO и других местах.Обычно доступны следующие параметры:

  • табличные параметры (только MSSQL 2008+),
  • динамически создаваемый SQL (подвержен ошибкам и / или небезопасен),
  • временные таблицы (хорошо для длинных списков, вероятно, слишком много накладных расходов при написании и времени выполнения для коротких),
  • строки с разделителями (хорошо для коротких списков «правильных» значений - как горстка целых чисел),
  • Параметры XML (несколько сложные, но хорошо работают - если вы используете хорошую библиотеку XML и не создаете сложный текст XML «вручную»).

Вот статья с хорошим обзором этих техник и несколькими другими.

2 голосов
/ 21 сентября 2010

Я обнаружил, что "NOT IN" и "INNER / LEFT JOIN" медленные и дорогие, когда базовая (крайняя левая) таблица велика

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

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

Зависит.SELECT function(x) FROM ..., вероятно, не будет иметь большого значения для производительности.Проблема заключается в том, что вы используете функцию столбца в других местах запроса, таких как условия JOIN, предложение WHERE или ORDER BY, так как это может означать, что индекс не может быть использован.Однако функция с постоянным значением не является проблемой.

Что касается вашего запроса, я бы сначала попытался использовать [col] NOT IN ('ABD', 'RDF', 'TRM', 'HYP', 'UOE').Если это происходит медленно, убедитесь, что вы правильно проиндексировали таблицу.

0 голосов
/ 21 сентября 2010

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

Так что остаетсяповторное использование плана запроса.

  • Если вы используете SQL Server 2008, замените @param1 на параметр с табличным значением, и ваше приложение передаст , что вместо списка с разделителями.Это полностью решит вашу проблему.

  • Если вы используете SQL Server 2005, я не думаю, что это имеет значение.Вы можете разделить список с разделителями и использовать NOT IN / NOT EXISTS против таблицы, но какой смысл, если вы не получите поиск по индексу col?

Кто-нибудь может сказать до последнего пункта?Будет ли разделение списка на таблицу var, а затем его анти-объединение сэкономить достаточно циклов ЦП, чтобы компенсировать стоимость установки?

EDIT , третий метод для SQL Server 2005 с использованием XML, вдохновленный OMGСсылка пони:

DECLARE @not_in_xml XML
SET @not_in_xml = N'<values><value>ABD</value><value>RDF</value></values>'

SELECT * FROM Table1 
WHERE @not_in_xml.exist('/values/value[text()=sql:column("col")]') = 0

Понятия не имею, насколько хорошо это работает по сравнению со списком с разделителями или TVP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...