loading..
Русский    English
13:20
листать

Предикат NOT IN

Рассмотрим еще один пример, позаимствованный мной у Селко [7]. Идея его состоит в использовании предиката NOT IN (<список значений, включающий NULL>).

Опять таки, для того чтобы вы могли проверить справедливость рассуждений на сайте, давайте искусственно добавим NULL-значения в результат запроса:

Консоль
Выполнить
  1. SELECT name, launched,
  2. CASE
  3. WHEN launched < 1915
  4. THEN NULL
  5. ELSE launched
  6. END year
  7. FROM Ships
  8. WHERE launched <= 1915;

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

name launched year
Hiei 1914 NULL
Kirishima 1915 1915
Kongo 1913 NULL

А теперь напишем запрос, который должен вернуть все корабли, год спуска на воду не находится в наборе значений столбца year:

Консоль
Выполнить
  1. SELECT *
  2. FROM Ships
  3. WHERE launched <= 1916 AND
  4. launched NOT IN(SELECT year
  5. FROM (SELECT name, launched,
  6. CASE WHEN launched < 1915
  7. THEN NULL
  8. ELSE launched
  9. END year
  10. FROM Ships
  11. WHERE launched <= 1915
  12. ) x
  13. );

Запрос

Консоль
Выполнить
  1. SELECT *
  2. FROM Ships
  3. WHERE launched <= 1915;
дает нам следующий набор кораблей:

name class launched
Hiei Kongo 1914
Kirishima Kongo 1915
Kongo Kongo 1913

Казалось бы, мы должны получить корабли Hiei и Kongo, так как год их спуска на воду известен и не равен 1915. Но нет, мы опять получаем пустой результирующий набор.

Оценим значение предиката для первого из этих кораблей — Hiei (для остальных все будет аналогично). Итак,

  1. 1914 NOT IN (1915, NULL)

Еще одно NULL-значение мы опустили для краткости. Последний предикат можно заменить следующим:

  1. 1914 <> ALL (1915, NULL)
что эквивалентно

  1. 1914 <> 1915
  2. AND
  3. 1914 <> NULL

Последнее выражение всегда равно UNKNOWN, следовательно, предикат можно переписать в виде:

  1. 1914 <> 1915
  2. AND
  3. UNKNOWN

Следовательно, и все выражение будет равно UNKNOWN, так как первое сравнение дает TRUE. Если бы первое сравнение было ложным (для 1915 года), то результат всего выражения был бы равен FALSE.

Поэтому можно сделать вывод, что при наличии NULL-значения в наборе предикат NOT IN в предложении WHERE всегда будет давать пустой набор записей.

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

Консоль
Выполнить
  1. SELECT *
  2. FROM Ships
  3. WHERE launched <= 1915
  4. UNION
  5. SELECT *
  6. FROM Ships
  7. WHERE launched > 1915;
не гарантирует восстановления исходной таблицы. Для этого потребуется еще один фрагмент, содержащий в столбце launched NULL-значения:

Консоль
Выполнить
  1. SELECT *
  2. FROM Ships
  3. WHERE launched IS NULL;

Bookmark and Share
Тэги:
ALL AND AUTO_INCREMENT AVG battles CASE CAST CHAR CHARINDEX CHECK classes COALESCE CONSTRAINT Convert COUNT CROSS APPLY CTE DATEADD DATEDIFF DATENAME DATEPART DATETIME DDL DEFAULT DELETE DISTINCT DML EXCEPT EXISTS EXTRACT FOREIGN KEY FROM FULL JOIN GROUP BY Guadalcanal HAVING IDENTITY IN INNER JOIN insert INTERSECT IS NOT NULL IS NULL ISNULL laptop LEFT LEFT OUTER JOIN LEN maker MAX Больше тэгов
Учебник обновлялся
несколько дней назад
©SQL-EX,2008 [Развитие] [Связь] [О проекте] [Ссылки] [Team]
Перепечатка материалов сайта возможна только с разрешения автора.
Rambler's Top100