SQL "в подзапросе", когда подзапрос может быть равен NULL

у меня есть запрос, который должен возвращать результаты, которые не совпадают в подзапросе. Суб-запрос может возвращать пустой результат, поэтому мне нужно установить значение по умолчанию (скажем, 0), если суб-запрос возвращает пустой набор для предотвращения IN (NULL) который всегда возвращает другой NULL.

SELECT * FROM example_table WHERE id NOT IN (subquery_that_selects_ids)

subquery_that_selects_ids может возвращать набор чисел, т. е. (1,2,5,6) или пустой набор, если подзапрос не находит совпадающих результатов.

COALESCE не работает здесь, так как sub query, скорее всего, вернет более одного результата.

решения должны работать в SQLite или postgresql. Как предотвратить возврат пустого набора вложенным запросом?


все говорят мне, что запрос должен работать как написано. И вы все правы. Запрос строится AREL Rails3, так как я собирался опубликовать полный запрос здесь, я заметил, что AREL вводит NULL для пустого набора при использовании условий массива.

И. Е. Моя запрос в Rails выглядел так:

Object.where("id NOT IN (?)", Object.where(other_conditions).select(:id))

, когда Object.where(other_conditions) оценка для [] на ? был заменен NULL

поэтому я переписываю запрос, чтобы выглядеть так:

Object.where("id NOT IN (" + Object.where(other_conditions).select(:id).to_sql + ")")

проблема решена.

Я даю кредит @Michael Buen, но также поддерживаю любого, кто сказал мне, что запрос будет работать как написано, так как они верны. Спасибо @OMG Ponies и @Ted Elliott особенно!

5 ответов


попробуй:

SELECT * FROM example_table 
WHERE id NOT 
    IN (select x.id from subquery_that_selects_ids as x where x.id is not null)

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

SELECT * FROM example_table 
WHERE id NOT 
    IN (select x.id from subquery_that_selects_ids as x 
        where 1 = 0 -- empty set
        union
        select 0)

UNION устраняет дубликат в любом случае, UNION ALL сохраняет дубликаты


о:

SELECT ex.ID, ex.OtherFields
FROM ExampleTable ex left join (Select ID from SomeOtherTable) o on o.ID = ex.ID
WHERE o.ID is null

Я думаю, что вы что-то путаете. Запрос, который вы разместили, работает отлично, если subquery_that_selects_ids возвращает пустой набор (поэтому каждая строка из example_table выбран). Здесь нет неявных значений null.

вы можете думать о ситуации, когда подзапрос используется в качестве скаляра. В этом случае значение результата равно null, если подзапрос не возвращает строк, например,

SELECT * FROM example_table WHERE id = (SELECT id FROM other_table WHERE name = 'foo')

в многозначном подзапросе возникнут проблемы, если вы используете оператор NOT in и если набор, возвращаемый подзапросом, содержит нулевое значение. Если подзапрос является пустым набором, это не означает, что он вернул null;)

в случае наличия значения null он заставляет внешний запрос возвращать один пустой набор, потому что он не может сказать, находится ли значение в неизвестном или нет в неизвестном.

вот один пример использования час.сотрудников таблица.

*выберите фамилия От hr.сотрудники Где commission_pct не в (0.1,0.35);*

этот запрос вернет 26 строк.

*выберите фамилия От hr.сотрудники Где commission_pct не в (0.1, 0.35, NULL);*

этот запрос не возвращает строк, потому что NULL в списке, переданном в NOT IN, испортит его. Таким образом, если ваш подзапрос может вернуть любое значение null, вы должны обработать его с помощью функций (NVL,NVL2, COALESCE) в подзапрос.

надеюсь, что это помогло.

спасибо

Александр Буфало


почему это не сработает?

SELECT *
  FROM example_table
 WHERE id IN (
    SELECT COALESCE(id, 0)
      FROM another_example_table
);