2012-02-09 4 views
1

Я использую следующий SQL в своей хранимой процедуре, чтобы не фильтровать по дате параметры, если они равны нулю.Фильтры параметров хранимой процедуры - Игнорировать, если Null

WHERE (Allocated >= ISNULL(@allocatedStartDate, '01/01/1900') 
     AND Allocated <= ISNULL(@allocatedEndDate,'01/01/3000')) 
AND 
(MatterOpened >= ISNULL(@matterOpenedStartDate, '01/01/1900') 
AND MatterOpened <= ISNULL(@matterOpenedEndDate, '01/01/3000')) 

Будет ли это приносить какое-либо влияние при работе с большим количеством записей?

Есть ли лучший способ сделать это?

Количество записей - около 500k

+0

Возможно, вы захотели поместить 'MatterOpened' (вместо' Allocated') в эту самую последнюю строку вашего запроса - правильно? –

ответ

3

Или просто пусть оптимизатор запросов есть это:

WHERE (@allocatedStartDate is NULL or Allocated >= allocatedStartDate) and 
    (@allocatedEndDate is NULL or Allocated <= @allocatedEndDate) and 
    (@matterOpenedStartDate is NULL or MatterOpened >= @matterOpenedStartDate) and 
    (@matterOpenedEndDate is NULL or MatterOpened <= @matterOpenedEndDate) 

Обратите внимание, что это не логически эквивалентно вашему запросу. В последней строке используется столбец MatterOpened, а не Allocated, поскольку я предполагаю, что это была типографская ошибка.

Если производительность действительно является проблемой, вы можете захотеть добавить индексы и изменить хранимую процедуру для выполнения различных запросов на основе параметров. По крайней мере разбить его на: нет фильтра, фильтровать только на выделенном, фильтровать только на MatterOpened, фильтровать на обоих столбцах.

+0

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

+0

@AaronBertrand - Согласовано. Если известно, что 82% * вызовов не выполняют фильтрацию, тогда имеет смысл иметь простой 'IF', который выбирает между нефильтрованным и полностью отфильтрованным запросом. Другая возможность - использовать 'WITH RECOMPILE' для оптимизации каждого запроса на основе параметров. Для принятия обоснованного решения необходимо проанализировать рабочую нагрузку и окружающую среду. (По совпадению, 82% всей статистики составлены на месте.) – HABO

0

Вместо того, чтобы проверять, если переменная равна нулю в запросе, проверить их в начале хранимой процедуры и изменить значение используемого по умолчанию

SELECT 
    @allocatedStartDate = ISNULL(@allocatedStartDate, '01/01/1900'), 
    @allocatedEndDate = ISNULL(@allocatedEndDate,'01/01/3000'), 
    @matterOpenedStartDate = ISNULL(@matterOpenedStartDate, '01/01/1900'), 
    @matterOpenedEndDate = ISNULL(@matterOpenedEndDate, '01/01/3000') 
+0

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

1

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

DECLARE @sql NVARCHAR(MAX); 

SET @sql = N'SELECT 
... 
WHERE 1 = 1'; 

SET @sql = @sql + CASE WHEN @allocatedStartDate IS NOT NULL THEN 
    ' AND Allocated >= ''' + CONVERT(CHAR(8), @allocatedStartDate, 112) + ''''; 

-- repeat for other clauses 

EXEC sp_executesql @sql; 

Нет, это не забавно поддерживать, но каждый вариант должен получить свой собственный план в кеше. Вы хотите протестировать с различными настройками для «Оптимизация для специальных рабочих нагрузок» и параметров параметризации на уровне базы данных. Ой, только что заметил 2005. Имейте это в виду на будущее (и любые читатели, которые еще не застряли в 2005 году).

Также не забудьте использовать EXEC sp_executesql, а не EXEC.

0

Может быть что-то вроде этого:

DECLARE @allocatedStartDate DATETIME=GETDATE() 
DECLARE @allocatedEndDate DATETIME=GETDATE()-2 

;WITH CTE AS 
(
    SELECT 
     ISNULL(@allocatedStartDate, '01/01/1900') AS allocatedStartDate, 
     ISNULL(@allocatedEndDate,'01/01/3000') AS allocatedEndDate 
) 
SELECT 
    * 
FROM 
    YourTable 
    CROSS JOIN CTE 
WHERE (Allocated >= CTE.allocatedStartDate 
     AND Allocated <= CTE.allocatedEndDate) 
AND 
(MatterOpened >= CTE.allocatedStartDate 
AND Allocated <= CTE.allocatedEndDate) 
Смежные вопросы