Выбрать главу

. ! .

Стандартный предикат SQL EXISTS (значение-подзапроса) и его противоположный аналог NOT EXISTS дают способ выполнения проверки существования набора, который является очень дешевым с точки зрения используемых ресурсов. Он не генерирует выходной набор, а просто проходит по таблице, пока не найдет строку, соответствующую условиям в подзапросе. В этот момент возвращается истина. Если же не находится ни одной строки, то возвращается ложь.

В первом примере EXISTS проверяет условия для выполнения изменения в операторе динамического SQL:

UPDATE TABLEA

SET COL6 ='SOLD'

WHERE COL1 =99

AND EXISTS(SELECT COLB FROM TABLEB WHERE COLB = 99);

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

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

UPDATE TABLEA

SET TABLEA.COL6 ='SOLD'

WHERE EXISTS(SELECT TABLEB.COLB FROM TABLEB

WHERE TABLEB.COLB = TABLEA.COL1) ;

Действием такого выражения существования будет: "Если в таблице TABLEB существует хотя бы одна соответствующая строка, то выполнить изменение".

Предикат IN

Предикат IN, используемый в подзапросе, похож на EXISTS постольку, поскольку он может проверять результат подзапроса. Например:

UPDATE TABLEA

SET COL6 ='SOLD'

WHERE COL1 IN (SELECT COLB FROM TABLEB

WHERE COL1 > 0);

В этом случае подзапрос возвращает набор всех значений столбца COLB второй таблицы, которые соответствуют его собственному предложению WHERE. Предикат IN приводит к сравнению COL1 С каждым возвращенным значением набора, пока не будет найдено соответствие. Он будет выполнять изменение каждой строки таблицы TABLEA, для которой значение COL1 соответствует значению в наборе.

! ! !

ПРИМЕЧАНИЕ. В Firebird предикат IN, который использует набор подзапроса, фактически реализуется предикатом EXISTS для операции сравнения.

. ! .

Ограничения

С точки зрения производительности предикат IN не будет полезен, когда подзапрос возвращает достаточно большое количество значений. Чаще он используется при формировании набора значений для сравнения из небольшой таблицы соответствия (lookup table) или из фиксированного набора констант, например:

UPDATE TABLEA SET C0L6 ='SOLD'

WHERE COL1 IN ('А', 'В', 'C', 'D');

Количество констант в наборе имеет ограничение максимум 1500 значений- возможно меньше, если значения велики по размеру, и размер строки запроса превышает его лимит в 64 Кбайт.

Предикат ALL

Его использование лучше проиллюстрировать, начав с примера:

SELECT * FROM MEMBERSHIP

WHERE

(EXTRACT (YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM DATE_OF_BIRTH))

< ALL (SELECT MINIMUM_AGE FROM AGE_GROUP) ;

Выражение в левой части вычисляет возраст в годах для каждого человека из таблицы MEMBERSHIP и выводит только тех людей, кто моложе минимального возраста в таблице MINIMUM AGE. Предикат ALL имеет ограниченное использование, поскольку он подходит только для поиска в случае сортировки от большего к меньшему при проверке исключения.

Предикат SINGULAR

Предикат SINGULAR похож на ALL, за исключением того, что он проверяет наличие одного и только одного соответствующего значения в наборе. Например, следующий запрос отыскивает все заказы, которые имеют только одну детальную строку:

SELECT OH.ORDER_ID FROM ORDER_HEADER OH

WHERE OH.ORDER_ID = SINGULAR(SELECT OD.ORDER_ID

FROM ORDER_DETAIL OD) ;

Предикаты ANY и SOME

Эти два предиката идентичны по поведению. Очевидно, оба представлены в стандарте SQL для взаимозаменяемого использования с целью улучшения читаемости операторов. При сравнении на равенство они логически эквивалентны предикату EXISTS. При этом, поскольку они предназначены и для других сравнений, таких как >, <, >=, <=, STARTING WITH, LIKE и CONTAINING, они в особенности полезны для проверок существования, где EXISTS не может быть использован.

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

SELECT E.EMP_NO, E.FULL_NAME, E.HIRE_DATE

FROM EMPLOYEE E

WHERE E.HIRE_DATE + 365 > SOME (

SELECT SH.CHANGE_DATE FROM SALARY_HISTORY SH

WHERE SH.EMP_NO = E.EMP_NO);

Алиасы таблиц

Обратите внимание на использование алиасов таблиц в последних двух примерах, что уменьшает двусмысленность в соответствующих именах столбцов двух таблиц. Firebird весьма требователен к использованию алиасов таблиц в запросах к нескольким таблицам. Алиасы таблиц обсуждаются в главе 22.

Обсуждение NULL

NULL может оказаться довольно сложным для людей, ранее работавших с настольными базами данных, которые просто использовали NULL для хранения "нулевых значений": пустая строка, ноль для чисел, ложь для логических столбцов и т.д. В SQL любой элемент данных в столбце, допускающем пустое значение (то есть в столбце, не имеющем ограничения NOT NULL), будет хранить элемент NULL, если ему не было присвоено никакого значения в операторе DML или в значении по умолчанию для столбца.

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

NULL в выражениях

NULL не является значением, следовательно, он не может быть "равным" какому-либо значению. Например, воз такой предикат

WHERE (COL1 = NULL)

вернет ошибку, потому что оператор равенства не является действительным для NULL, NULL является состоянием, и правильным предикатом проверки на NULL будет IS NULL. Скорректированный предикат для предыдущего ошибочного тестирования будет иметь вид:

WHERE (COL1 IS NULL)

Вы также можете выполнять проверку на непустое значение:

WHERE (COL1 IS NOT NULL)

Два NULL не равны один другому. При конструировании выражений помните о тех случаях, когда предикат будет сведен к виду:

WHERE <NULL result> = <NULL result>

Здесь результатом всегда будет ложь при сравнении двух NULL. Выражение типа

WHERE COL1 > NULL

будет ошибочным, потому что арифметический оператор недопустим для NULL.

NULL в вычислениях

В выражении пустой операнд даст результат вычисления NULL. Например, следующий оператор

UPDATE TABLEA

SET COL4 = COL4 + C0L5;

присвоит столбцу COL4 значение NULL, если значением COL5 является NULL.

В агрегатном (обобщающем) выражении, использующем операторы типа SUMO, AVG() или COUNT (<ИМЯ столбца>), строки, содержащие NULL В соответствующем столбце, будут проигнорированы, AVG() сформирует числитель, суммируя непустые значения, и знаменатель, подсчитывая строки, содержащие непустые значения.

Понимание истинности и ложности

Семантически, если предикат возвращает "неопределенность", это не является ни истиной, ни ложью. В SQL при этом утверждения разрешаются только в виде "истина" или "ложь" - утверждение, которое не вычисляется как "истина", рассматривается как "ложь".

Условие IF, неявно присутствующее в предикатах поиска, может вызвать у вас галлюцинацию, когда NOT используется во вложенном условии: