SQL не равно & null

мы хотели бы написать этот запрос:

select * from table 
where col1 != 'blah' and col2 = 'something'

мы хотим, чтобы запрос включал строки, где col1 равно null (и col2 = 'something'). В настоящее время запрос не будет делать этого для строк, где col1 равно null. Является ли запрос ниже лучшим и самым быстрым способом?

select * from table 
where (col1 != 'blah' or col1 is null) and col2 = 'something'

кроме того, мы могли бы при необходимости обновить все значения col1 null до пустых строк. Будет ли это лучшим подходом? Тогда наш первый запрос сработает.


обновление: Re: использование NVL: я читал на другом в должности что это не является отличным вариантом с точки зрения производительности.

9 ответов


в Oracle нет разницы между пустой строкой и NULL.

Это вопиющее пренебрежение стандартом SQL, но там вы идете ...

кроме того, вы не можете сравнивать с NULL (или not NULL) с "нормальными" операторами: "col1 = null" не будет работать, "col1 = "" не будет работать, "col1 != null "не будет работать, вы должны использовать "is null".

итак, нет, вы не можете сделать эту работу каким-либо другим способом, тогда "col 1 равен null" или некоторая вариация на это (например, использование nvl).


Я думаю, что решение, которое вы выложили, является одним из лучших вариантов.

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

Если вы спросите меня, я не буду использовать пустую строку в качестве null, но может быть просто личным предпочтение.


пока не самый читаемый-Oracle имеет функция LNNVL это по существу функция not (), но инвертирует поведение для нулей. Это означает, что сравнение чего-либо с null внутри lnnvl вернет true (я не знаю, какие последствия для производительности это может иметь).

делать то, что вы хотите в одном заявлении:

select * from table where lnnvl(col1 = 'blah') and col2 = 'something'

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

lnnvl(nvl(col1, -1) = nvl(col2, -1))

Это зависит от ваших данных, но большинство оптимизаторов будут смотреть на col2 перед col1, так как = - это более простой индекс, чем !=.

в противном случае есть различные способы ускорить этот запрос. Это, вероятно, лучше всего сделать (col1 != 'blah' или col1 равно null), но некоторые базы данных позволяют индексировать функцию. Таким образом, вы можете индексировать coalesce(col1, 0) и получить хорошую производительность.

действительно это зависит от вас данных и вашей таблицы.


в oracle используйте функцию nvl

select * from table where nvl(col1,'notblah') <> 'blah'

Если вы хотите ускорить такой запрос, и вы находитесь на Oracle 10g или более поздней версии, используйте функциональный индекс, чтобы превратить эти нули в значения:

CREATE INDEX query_specific_index ON table (col2, NVL(col1,'***NULL***'));

select * from table 
where NVL(col1,'***NULL***') != 'blah' and col2 = 'something';

база данных, скорее всего, будет использовать индекс в этом сценарии (конечно, при условии решения CBO, зависит от количества строк и точности статистики). Запрос должен использовать точное выражение, указанное в индексе - в этом случае"NVL(col1,'***NULL***')"

конечно, выберите значение '***NULL***' что будет не конфликтует с любыми данными в col1!


как насчет этого варианта. Я думаю, что это может сработать, если ваше значение никогда не равно null.

where not (value = column)

что привело бы к следующей таблице истинности для оценки для предложения where

                    col1
              | 'bla' |  null |
              -----------------
      | 'bla' |   F   |   T   |
value -------------------------
      |  null |   T   |  *T   | 

*это единственный "неправильный", но это нормально, так как наше значение никогда не равно null

обновление

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

select 'x', 'x' from dual where not ('x' = 'x');
0 rows
select 'x', 'y' from dual where not ('x' = 'y');
1 row
select 'x', 'null' from dual where not ('x' = null);
0 rows
select 'null', 'null' from dual where not (null = null);
0 rows

обновление 2

это решение работает, если ваше значение никогда не равно null (соответствует таблице истинности выше)

where ('blah' != col1 or col1 is null)

тесты здесь:

select 'x', 'x' from dual where ('x' != 'x' or 'x' is null);
0 rows
select 'x', 'y' from dual where ('x' != 'y' or 'y' is null);
1 row
select 'x', 'null' from dual where ('x' != null or null is null);
1 row
select 'null', 'null' from dual where (null != null or null is null);
1 row

Для Oracle

select * from table where nvl(col1, 'value') != 'blah' and col2 = 'something'

Для SqlServer

select * from table where IsNull(col1, '') <> 'blah' and col2 = 'something'

Я думаю, что ваше увеличение будет минимальным при изменении нулевых значений на "" строки. Однако, если 'blah' не является null, то он должен включать нулевые значения.

EDIT: наверное, я удивлен, почему меня проголосовали здесь. Если "бла", если не null или пустая строка, то это никогда не должно иметь значения, поскольку вы уже проверяете, не равен ли COL1 "БЛА", который не является нулем или пустой строкой.