Как проверить поле с переменной SQL, которая может быть нулевой?

У меня есть следующий 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

Следующий запрос не возвращает ожидаемые результаты select * from tbFoo, где a = @ a, потому что я должен использовать оператор «is», а не «=»,

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

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

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

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

CREATE UNIQUE NONCLUSTERED INDEX IX_tbFoo_a 
ON tbFoo (a)
WHERE a IS NOT NULL;

Правильно ли мой анализ ситуации?

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

sql,sql-server,sql-server-2008,three-valued-logic,

3

Ответов: 8


3

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

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

set ansi_nulls off

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

select * from tbFoo where a=@a

set ansi_nulls on

1

Ничто никогда не «равно» NULL ... это своего рода точка NULL.

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


1

Другая возможность заключается в установке значений ansi nulls

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

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


1

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

if @a IS NULL

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


0

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

set @a = isnull(@a,'somethingweird')
select * from tbFoo where isnull(a,'somethingweird')=@a
SQL, SQL-сервер, SQL-сервер-2008, трехзначной логики,
Похожие вопросы