Лучший способ отфильтровать запросы по параметру? - PullRequest
0 голосов
/ 25 марта 2009

Я использовал этот метод для фильтрации своих запросов:

Create PROCEDURE [dbo].[pGetTask]
    @showCompletedTasks bit = 1
    ,@showInProgressTasks bit = 1
    ,@taskID int = null
    ,@projectID int = null
    ,@applicationID int = null
    ,@clientID int = null

... Snip ...

where       
    a.clientID = isnull(@clientID, a.clientID)
    and a.applicationID = isnull(@applicationID, a.applicationID)
    and p.projectID = isnull(@projectID, p.projectID)
    and t.taskID = isnull(@taskID, t.taskID)
    and curr.complete = case @showCompletedTasks when 0 then 0 else curr.complete end
    and curr.complete = case @showInProgressTasks when 0 then 1 else curr.complete end

Это на самом деле замедляет мои запросы на 2 секунды в наборе результатов из 664 строк. Советник по настройке SQL не очень помогает, поэтому я считаю, что это неправильный способ сделать это. Есть ли правильный путь, кроме тонны утверждений if?

Ответы [ 6 ]

6 голосов
/ 25 марта 2009

Предполагая, что вы правильно проиндексировали таблицу, в которой находится select, и эти поля являются частью индекса, я предполагаю, что это будут вызовы isnull. Я бы изменил их на это:

(@clientID is null or a.clientID = @clientId) and ...

Что касается операторов case, индексы в битовых полях не имеют смысла, так что там особо нечем заняться.

2 голосов
/ 25 марта 2009

Проверьте ваши индексы и статистику. Это кажется немного медленным. Другой вариант - сделать динамический запрос, по сути, построить строку, представляющую ваш sql, и выполнить ее, используя sp_ExecuteSql (или оператор Exec)

Редактировать

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

Хотя я не уверен, что ваш запрос верный (что трудно сказать без дополнительной информации), но не должно быть ни одного, ни предложения между случаями, когда вы пытаетесь предоставить два состояния для возврата, и имея отдельные параметры, я полагаю Я могу попросить Только Завершено, Только Не Завершено или оба ... в этом случае вам нужен Ор

1 голос
/ 08 апреля 2009

Вы можете стать жертвой проблемы "сниффинга параметров". MS-SQL примет параметры вашего первого прогона вашего SP как лучшую выборку для составления плана запроса. Ваш запрос может быть медленным из-за этого.

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

Решение состоит в том, чтобы обмануть MS-SQL, чтобы думать, что ваши параметры используются только для назначения другим переменным. Пример:


create proc ManyParams
(
    @pcol1 int,
    @pcol2 int,
    @pcol3 int
)
as
declare
    @col1 int,
    @col2 int,
    @col3 int

select
    @col1 = @pcol1,
    @col2 = @pcol2,
    @col3 = @pcol3

select 
    col1,
    col2,
    col3
from 
    tbl 
where 
    1 = case when @col1 is null then 1 else case when col1 = @col1 then 1 else 0 end end
and 1 = case when @col2 is null then 1 else case when col2 = @col2 then 1 else 0 end end
and 1 = case when @col3 is null then 1 else case when col3 = @col3 then 1 else 0 end end

1 голос
/ 25 марта 2009

Лучше всего использовать эту хранимую процедуру для вызова ряда более конкретных процедур. У вас есть две проблемы:

  1. Использование описания дела вызывает сканирование таблицы, которая (очевидно) игнорирует любые индексы, которые вы может иметь
  2. Даже если вы сломаете заявление на несколько, которые условно, ты все равно закончить скомпилированным исполнением план, который является специфическим для первого вызов этой процедуры.

Если вы создаете определенные процедуры, такие как pGetTask_Completed и pGetTask_InProgress, и вызываете их условно из этого процесса, у вас не должно быть никаких проблем.

0 голосов
/ 25 марта 2009

Предложение casperOne - это то, с чего я бы начал.

Еще одна возможность:

WHERE
     (1 =
     CASE
         WHEN @client_id IS NULL THEN 1
         WHEN a.clientID = @clientID THEN 1
         ELSE 0
     END) AND
...

Я обнаружил, что SQL Server (по крайней мере, 2005) с использованием оператора CASE, подобного этому, может привести к тому, что план запроса закоротит остальную логику. В случае простых сравнений это не является большой проблемой, но если ваша логика включает в себя подзапрос или какую-либо другую дорогостоящую операцию, это может быть большим подспорьем для его короткого замыкания. В вашем примере я бы просто согласился с предложением casperOne.

Кроме того, если вы используете метод CASE, описанный выше, вам необходимо добавить опции RECOMPILE в ваш SELECT и вашу хранимую процедуру.

0 голосов
/ 25 марта 2009

вот отличная статья на эту тему:

http://www.sommarskog.se/dyn-search-2005.html

Это даст вам много идей для опробования.

Я склоняюсь к тому, чтобы эти запросы типа поиска выполнялись быстро. Вот некоторые из них, которые я, похоже, постоянно использую:

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

  • если возможно (зависит от количества строк), разделите запрос, используя временные таблицы. Если у вас есть только несколько сотен значений ClientID, создайте временную таблицу #ClientID. Вставьте тот, который хочет пользователь, или все из них. Затем вы можете сделать эту таблицу FROM и / или внутренне объединить другие таблицы, чтобы сделать ее намного быстрее.

  • Если у вас есть даты, которые являются необязательными, не используйте ничего подобного (@startDate имеет значение null или a.date> = @startDate). Просто сделайте что-то вроде SET @ startDate = COALESCE (@ startDate, '01 / 01/1970 '). Это даст вам значение и исключит использование «ИЛИ» и будет использовать индекс.

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