Как я должен проверить поле с переменной SQL, которая может быть нулевой? - PullRequest
3 голосов
/ 16 июня 2009

У меня есть следующий SQL:

CREATE TABLE tbFoo(
    a varchar(50) NULL,
) 


CREATE NONCLUSTERED INDEX IX_tbFoo_a ON tbFoo
(
    a ASC
)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)

insert into tbFoo select null
insert into tbFoo select 'test'

Два следующих запроса работают нормально и используют мой индекс, как и ожидалось:

select * from tbFoo where a='test'
select * from tbFoo where a is null

Теперь давайте представим, что я хочу сохранить мое значение сравнения в переменной, например:

declare @a varchar(50)
select @a = NULL

Следующий запрос не вернет ожидаемых результатов, если @a имеет значение null, потому что я должен использовать оператор "is" вместо "="

select * from tbFoo where a=@a 

Следующее будет работать, но будет выполнять сканирование таблицы, если @a равно нулю (из-за строки 'test', которая вызывает вычисление второй круглой скобки)

select * from tbFoo where (a is null and @a is null) or (a=@a)

В конце концов, я нашел это решение, которое отлично работает и использует мой индекс:

select * from tbFoo where (a is null and @a is null) or (@a is not null and a=@a)

Является ли мой анализ ситуации правильным?

Есть ли лучший способ справиться с этой ситуацией?

Ответы [ 8 ]

3 голосов
/ 16 июня 2009

В конце концов, я пришел к этому решению, которое отлично работает и использует мой индекс:

В SQL Server 2008 вы можете определить отфильтрованный индекс на основе предиката, который исключает NULL:

CREATE UNIQUE NONCLUSTERED INDEX IX_tbFoo_a 
ON tbFoo (a)
WHERE a IS NOT NULL;
1 голос
/ 17 июня 2009

Возможно, ваш движок базы данных оптимизирует то, что вы получили автоматически, но мне кажется, что следующее будет более эффективным:

if @a IS NULL
    select * from tbFoo where a is null
else
    select * from tbFoo where a = @a

Я считаю, что вы должны выполнить условие if @a IS NULL только один раз, а не проверять его для каждой строки в базе данных. Опять же, однако, качественный движок базы данных должен быть в состоянии преобразовать ваш код в тот же тип плана данных, что и этот.

1 голос
/ 16 июня 2009

другая возможность - отключить ANSI NULL

set ansi_nulls off

declare @a varchar(50)
select @a = NULL

select * from tbFoo where a=@a

set ansi_nulls on

Просто имейте в виду, что вы отрываетесь от поведения по умолчанию здесь

1 голос
/ 16 июня 2009

Ничто никогда не "равно" NULL ... что-то вроде точки NULL.

Ваше решение будет работать нормально. Я удивлен тем, как оптимизатор запросов работает с более короткой версией. Я бы подумал, что проверка на NULL перед проверкой равенства с помощью сканирования таблицы была бы легкой задачей.

0 голосов
/ 17 июня 2009

Ваш анализ верен - и именно поэтому трехзначная логика усложняет жизнь.

Предложение от @StriplingWarrior хорошее; он решает проблему, выполняя различные SQL в зависимости от того, является ли переменная нулевой или нет. Там, где это невозможно, необходимо ваше затянувшееся решение, которое многократно использует переменную хоста.

0 голосов
/ 17 июня 2009

Просто ИЗНУТРИ обе стороны, вот так ...

DECLARE @random VARCHAR(50)
SELECT  @random = 'text that never appears in your table'

SELECT * FROM @tbFoo WHERE ISNULL(a, @random) = ISNULL(@a, @random)
0 голосов
/ 17 июня 2009

У меня дома нет экземпляра, с которым можно играть, но я вижу, что сканирование стола становится очень раздражающим. Возможной альтернативой является использование UNION вместо оператора OR ...

select * from tbFoo where (a is null and @a is null)
UNION ALL
select * from tbFoo where (a=@a and @a is not null)

(Я не уверен, какое именно влияние «@a is null» окажет на производительность, но мое внутреннее чувство должно было бы включить его. Это постоянное выражение, которое должно позволить оптимизатору знать, когда все условие всегда терпит неудачу. Моя техника всегда играть и смотреть, что работает лучше всего.)

Я считаю, что этот трюк UNION имеет два свойства:
- Это может значительно улучшить производительность, упрощая запросы
- Это раздувает код с несколькими соединениями и вызывает серьезные проблемы с обслуживанием

Но тогда жизнь - это просто уравновешивающий акт:)

0 голосов
/ 16 июня 2009

Это то, что я делаю. Это очень гибкий. Я предполагаю, что @a является аргументом для sproc. 'whatweird' может быть чем-то, что вы никогда не увидите в своем наборе записей '~~~' или чем-то еще.

set @a = isnull(@a,'somethingweird')
select * from tbFoo where isnull(a,'somethingweird')=@a
...