Конструкции оператора SELECT языка SQL в значительной степени ортогональны. В частности, выбор способа указания ссылки на таблицы в разделе FROM никак не влияет на выбор варианта формирования условия выборки в разделе WHERE. Это полезное свойство языка позволяет нам абстрагироваться от обсуждавшегося в предыдущей лекции многообразия способов указания ссылки на таблицу и сосредоточиться на возможностях формирования запросов при использовании различных
В ); что одно SUBMULTISET ) и что IS A SET ). В этом курсе мы не приводим подробного описания этих видов
В лекции содержится много примеров запросов с использованием различных видов
Синтаксически
predicate ::= comparison_predicate
| between_predicate
| null_predicate
| in_predicate
| like_predicate
| similar_predicate
| exists_predicate
| unique_predicate
| overlaps_predicate
| quantified_comparison_predicate
| match_predicate
| distinct_predicate
Далее мы будем последовательно обсуждать разные виды СЛУЖАЩИЕ-ОТДЕЛЫ-ПРОЕКТЫ, определения таблиц которой на языке SQL были приведены в лекции 2. Для удобства повторим здесь структуру таблиц.
EMP_NO : EMP_NO |
EMP_NAME : VARCHAR |
EMP_BDATE : DATE |
EMP_SAL : SALARY |
DEPT_NO : DEPT_NO |
PRO_NO : PRO_NO |
DEPT_NO : DEPT_NO |
DEPT_NAME : VARCHAR |
DEPT_EMP_NO : INTEGER |
DEPT_TOTAL_SAL : SALARY |
DEPT_MNG : EMP_NO |
PRO_NO : PRO_NO |
PRO_TITLE : VARCHAR |
PRO_SDATE : DATEP |
PRO_DURAT : |
PRO_MNG : EMP_NO |
PRO_DESC : |
Столбцы EMP_NO, DEPT_NO и PRO_NO являются , DEPT и соответственно. Столбцы DEPT_NO и PRO_NO таблицы являются внешними ключами, ссылающимися на таблицы DEPT и соответственно ( DEPT_NO указывает на отделы, в которых работают служащие, а PRO_NO - на проекты, в которых они участвуют; оба столбца могут принимать неопределенные значения). Столбец DEPT_MNG является DEPT ( DEPT_MNG указывает на служащих, которые исполняют обязанности руководителей отделов; у отдела может не быть руководителя, и один служащий не может быть руководителем двух или более отделов). Столбец PRO_MNG является ( PRO_MNG указывает
на служащих, которые являются
Этот
comparison_predicate ::=
row_value_constructor comp_op row_value_constructor
comp_op ::= = | <> ("неравно")| < | >
| <= "меньше или равно"| >= "больше или равно"
Строки, являющиеся операндами операции сравнения, должны быть одинаковой степени. Типы данных соответствующих значений строк-операндов должны быть совместимы.
Пусть X и Y обозначают соответствующие элементы строк-операндов, а xv и yv - их значения. Тогда:
xv и/или yv являются неопределенными значениями, то значение условия X comp_op Y -unknown ;X comp_op Y является true или false в соответствии с естественными правилами применения операции сравнения.При этом:
X не равна длине строки Y, то для выравнивания длин строк более короткая строка расширяется символами набивки ( pad symbol ); если для используемого X и Y основано на сравнении соответствующих бит. Если Xi и Yi - значения i -тых бит X и Y соответственно и если lx и ly обозначает длину в битах X и Y соответственно, то:X равно Y тогда и только тогда, когда lx = ly и Xi = Yi для всех i ;X меньше Y тогда и только тогда, когда (a) lx < ly и Xi = Yi для всех i меньших или равных lx, или (b) Xi = Yi для всех i < n и Xn = 0, а Yn =1 для некоторого n меньшего или равного min (lx, ly).X и Y - сравниваемые значения, а H - наименее значимое поле даты-времени X и Y. Результат сравнения X comp_op Y определяется как (X - Y) H comp_ op INTERVAL (0) H. (Два значения Rx и Ry обозначают строки-операнды, а Rxi и Ryi - i -тые элементы Rx и Ry соответственно. Вот как определяется результат сравнения Rx comp_op Ry:Rx = Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i ;Rx <> Ry есть true тогда и только тогда, когда Rxi <> Ryi есть true для некоторого i ;Rx < Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i < n, и Rxn < Ryn есть true для некоторого n ;Rx > Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i < n, и Rxn > Ryn есть true для некоторого n ;Rx <= Ry есть true тогда и только тогда, когда Rx = Ry есть true или Rx < Ry есть true ;Rx >= Ry есть true тогда и только тогда, когда Rx = Ry есть true или Rx > Ry есть true ;Rx = Ry есть false тогда и только тогда, когда Rx <> Ry есть true ;Rx <> Ry есть false тогда и только тогда, когда Rx = Ry есть true ;Rx < Ry есть false тогда и только тогда, когда Rx >= Ry есть true ;Rx > Ry есть false тогда и только тогда, когда Rx <= Ry есть true ;Rx <= Ry есть false тогда и только тогда, когда Rx > Ry есть true ;Rx >= Ry есть false тогда и только тогда, когда Rx < Ry есть true ;Rx comp_op Ry есть unknown тогда и только тогда, когда Rx comp_op Ry не есть true или false.SELECT DISTINCT EMP.DEPT_NO FROM EMP WHERE EMP.EMP_NAME = 'Smith';
Мы добавили спецификацию ):
SELECT EMP.DEPT_NO, COUNT(*) FROM EMP WHERE EMP.NAME = 'Smith' GROUP BY EMP.DEPT_NO;
В этом варианте запроса спецификация не требуется, поскольку в запросе содержится раздел GROUP BY, группировка производится в соответствии со значениями столбца , и строка результата соответствует одной группе.
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO FROM EMP WHERE EMP.EMP_BDATE > DATE '1965-04-15';
В результате этого запроса дубликатов быть не может, поскольку в список выборки включен столбец, являющийся первичным ключом таблицы . Должно быть ясно, что по этой причине все строки результата будут различными.
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO
FROM EMP
WHERE EMP.EMP_SAL > 0.1 *
(SELECT DEPT_TOTAL_SAL
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO);
В этом WHERE этого DEPT. Во-вторых, в условии раздела WHERE , указанной в разделе FROM "внешнего" запроса. Подобные SELECT. В стандарте, естественно, не требуется, чтобы в
При выполнении внешнего запроса последовательно, строка за строкой, в некотором порядке, определяемом системой, производится проверка соответствия строк результирующей FROM условию раздела WHERE. Если это условие включает WHERE любого
Кстати, эквивалентная формулировка на языке SQL примера 14.3 выглядит следующим образом ():
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO FROM EMP, DEPT WHERE EMP.DEPT_NO = DEPT.DEPT_NO AND EMP.EMP_SAL > 0.1 * DEPT.TOTAL_SAL;
Мы видим, что ) эквисоединения таблиц и DEPT (по условию ). Подобную операцию часто называют
SELECT EMP1.EMP_NO, EMP1.EMP_NAME,
EMP1.DEPT_NO, EMP2.EMP_NAME
FROM EMP AS EMP1, EMP AS EMP2, DEPT
WHERE EMP1.EMP_SAL < 15000.00 AND
EMP1.DEPT_NO = DEPT.DEPT_NO AND
DEPT.DEPT_MNG = EMP2.EMP_NO;
Этот запрос представляет собой эквисоединение ограничения таблицы .
Покажем способ формулировки этого запроса с использованием вложенного
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO,
(SELECT EMP_NAME
FROM EMP
WHERE EMP_NO = DEPT_MNG)
FROM EMP, DEPT
WHERE EMP.EMP_SAL < 15000.00 AND
EMP.DEPT_NO = DEPT.DEPT_NO;
Как показывает последний пример, в условии выборки WHERE (если в запросе отсутствуют разделы GROUP BY и HAVING, случай (a)) или выполнения явно или неявно заданного раздела HAVING (случай (b)) выполняется раздел SELECT. При выполнении этого раздела на основе таблицы T1 в случае (a) или на основе сгруппированной таблицы T3 в случае (b) строится таблица T4, содержащая столько строк, сколько строк или групп строк содержится в таблицах T1 или T3 соответственно". В действительности, в общем случае очередная строка таблицы T4 должна строиться в тот момент, когда очередная строка или группа строк заносится в таблицу T1 или T3 соответственно.
between_predicate ::=
row_value_constructor [ NOT ] BETWEEN
row_value_constructor AND row_value_constructor
Все три строки-операнды должны иметь одну и ту же степень. Типы данных соответствующих значений строк-операндов должны быть совместимыми.
Пусть X, Y и Z обозначают первый, второй и третий операнды. Тогда по определению выражение X NOT BETWEEN Y AND Z NOT (X BETWEEN Y AND Z). Выражение X BETWEEN Y AND Z по определению эквивалентно X >= Y AND X <= Z.
SELECT EMP_NO, EMP_NAME, EMP_SAL FROM EMP WHERE EMP_SAL BETWEEN 12000.00 AND 15000.00;
SELECT EMP_NO, EMP_NAME, EMP_SAL
FROM EMP
WHERE EMP_SAL BETWEEN
(SELECT AVG(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO)
AND
(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP1.EMP_NO =
(SELECT DEPT.DEPT_MNG
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO));
В этом запросе можно выделить три интересных момента. Во-первых, диапазон значений предиката задан двумя подзапросами, результатом каждого из которых является единственное значение. Первый ) и отсутствует раздел GROUP BY, а второй - потому что в его разделе WHERE присутствует условие, задающее единственное значение получает EMP1 (в формулировке этого запроса мы старались использовать как можно меньше вспомогательных идентификаторов). Поскольку WHERE
используется ссылка на столбец таблицы из самого внешнего раздела FROM.
Предикат позволяет проверить, являются ли неопределенными значения всех элементов строки-операнда:
null_predicate ::= row_value_constructor IS [ NOT ] NULL
Пусть X обозначает строку-операнд. Если значения всех элементов X являются неопределенными, то значением условия X IS NULL является true ; иначе - false. Если ни у одного элемента X значение не является неопределенным, то значением условия X IS NOT NULL является true ; иначе - false.
Замечание: условие X IS NOT NULL имеет то же значение, что условие NOT X IS NULL для любого X в том и только в том случае, когда степень X равна 1. Полная null приведена в таблице 14.1.
| Вид операнда | Вид условия | |||
|---|---|---|---|---|
X IS X NULL |
IS NOT NULL |
NOT X IS NULL |
NOT X IS NOT NULL |
|
Степень 1: значение NULL |
true |
false |
false |
true |
Степень 1: значение отлично от NULL |
false |
true |
true |
false |
Степень > 1: у всех элементов значение NULL |
true |
false |
false |
true |
Степень > 1: у некоторых(не у всех) элементов значение NULL |
false |
false |
true |
true |
Степень > 1: ни у одного элемента нет значения NULL |
false |
true |
true |
false |
На самом деле, в нашей формулировке запроса из примера 14.6 есть одна неточность. Если у некоторого служащего номер отдела неизвестен (значение столбца у соответствующей строки таблицы служащих является неопределенным), то бессмысленно вычислять средний размер зарплаты отдела этого служащего и находить размер зарплаты руководителя отдела. Формулировка из примера 14.6 приведет к правильному результату, но это s - текущая строка таблицы , просматриваемой в цикле внешнего запроса, и пусть s.DEPT_NO содержит неопределенное значение. Тогда для строки s условие первого NULL = EMP1.DEPT_NO, и значением этого условия будет unknown для любой строки таблицы ( EMP1 ),
просматриваемой в цикле этого unknown не является разрешающим условием, результирующая таблица выдаст значение NULL. По этому поводу значением условия внешнего запроса будет unknown, и строка s не войдет в результирующую таблицу.IS NOT NULL и переписать запрос следующим образом:
SELECT EMP_NO, EMP_NAME, EMP_SAL
FROM EMP
WHERE DEPT_NO IS NOT NULL AND
EMP_SAL BETWEEN
(SELECT AVG(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO)
AND
(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP1.EMP_NO =
( SELECT DEPT.DEPT_MNG
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO ) );
SELECT EMP_NO, EMP_NAME FROM EMP WHERE DEPT_NO IS NULL;
in_predicate ::= row_value_constructor [ NOT ]
IN in_predicate_value
in_predicate_value ::= table_subquery
| (value_expression_comma_list)
Строка, являющаяся первым операндом, и таблица-второй операнд должны быть одинаковой степени. В частности, если второй операнд представляет собой список значений, то первый операнд должен иметь степень 1. Типы данных соответствующих столбцов операндов должны быть совместимы.
Пусть X обозначает строку-первый операнд, а S - множество строк второго операнда. Обозначим через s строку-элемент этого множества. Тогда по определению условие X IN S эквивалентно ORsS (X = s). Другими словами, X IN S принимает значение true в том и только в том случае, когда во множестве S существует хотя бы один элемент s, такой, что значением X = s является true. X IN S принимает значение false в том и только том случае, когда для всех элементов s множества S значением операции сравнения X = s является false. Иначе значением условия X IN S является unknown. Заметим, что для пустого множества S значением X IN S является false.
По определению условие X NOT IN S эквивалентно NOT (X IN S).
SELECT EMP_NO, EMP_NAME, DEPT_NO FROM EMP WHERE DEPT_NO IN (15, 17, 19);
Конечно, эта формулировка запроса эквивалентна следующей формулировке ():
SELECT EMP_NO, EMP_NAME, DEPT_NO FROM EMP WHERE DEPT_NO = 15 OR DEPT_NO = 17 OR DEPT_NO = 19;
SELECT EMP_NO
FROM EMP
WHERE EMP_NO NOT IN (SELECT DEPT_MNG FROM DEPT)
AND EMP_SAL IN (SELECT EMP_SAL FROM EMP,
DEPT WHERE EMP_NO = DEPT_MNG);
Запросы, содержащие предикат ):
SELECT DISTINCT EMP_NO FROM EMP, EMP EMP1, DEPT WHERE EMP_NO NOT IN (SELECT DEPT_MNG FROM DEPT) AND EMP_SAL = EMP1_SAL AND EMP1.EMP_NO = DEPT.DEPT_MNG;
По поводу этой второй формулировки следует сделать два замечания. Во-первых, как видно, мы изменили только ту часть условия, в которой использовался предикат , и не затронули предикат NOT IN. Запросы с предикатами NOT IN запросами с соединениями так просто не заменяются. Во-вторых, в разделе SELECT было добавлено ключевое слово , потому что в результате запроса во второй формулировке для каждого служащего будет содержаться столько строк, сколько существует руководителей отделов, получающих такую же зарплату, что и данный служащий.
Формально предикат определяется следующими синтаксическими правилами:
like_predicate ::= source_value [ NOT ]
LIKE pattern_value [ ESCAPE escape_value ]
source_value ::= value_expression
pattern_value ::= value_expression
escape_value ::= value_expression
Все три операнда ( source_value, pattern_value и escape_value ) должны быть одного типа: либо . Битовые строки типов BIT и BIT не допускаются.true в том и только в том случае, когда исходная строка ( source_value ) может быть сопоставлена с заданным шаблоном ( pattern_value ).
Если обрабатываются условия отсутствует, то при сопоставлении шаблона со строкой производится специальная интерпретация двух символов шаблона: символ подчеркивания (' _ ') обозначает любой одиночный символ; символ процента (' % ') обозначает последовательность произвольных символов произвольной длины (длина последовательности может быть нулевой). Если же раздел присутствует и специфицирует некоторый одиночный символ x, то пары символов " x_ " и " x% " представляют одиночные символы " _ " и " % " соответственно.
В случае обработки битовых строк сопоставление шаблона со строкой производится восьмерками соседних бит ( октетами ). В соответствии со X'25' и X'5F' (X'25' и X'5F'.
Значение предиката есть unknown, если значение первого или второго операндов является неопределенным. Условие x NOT LIKE y эквивалентно условию NOT x LIKE y .
SELECT PRO_TITLE
FROM PRO
WHERE PRO_TITLE LIKE '%next%step%'
OR PRO_TITLE LIKE 'Next%step%';
Это очень неудачный запрос, потому что его выполнение, скорее всего, вынудит СУБД просмотреть все строки таблицы ):
SELECT PRO_TITLE FROM PRO WHERE PRO_TITLE LIKE '%ext%step%';
SELECT DISTINCT DEPT.DEPT_NO FROM EMP, DEPT, PRO WHERE EMP.EMP_NO = PRO.PRO_MNG AND EMP.DEPT_NO = DEPT.DEPT_NO AND PRO.PRO_TITLE LIKE DEPT.DEPT_NAME || '%';
Вот как может выглядеть формулировка этого запроса, если использовать вложенные
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE DEPT.DEPT_NO IN
(SELECT EMP.DEPT_NO
FROM EMP
WHERE EMP.EMP_NO IN
(SELECT PRO.PRO_MNG FROM PRO
WHERE PRO.PRO_TITLE LIKE DEPT.DEPT_NAME || '%'));
SELECT DEPT_NO FROM DEPT WHERE DEPT_NAME NOT LIKE 'Software%';
Формально предикат определяется следующими синтаксическими правилами:
similar_predicate ::= source_value [ NOT ]
SIMILAR TO pattern_value [ ESCAPE escape_value ]
source_value ::= character_expression
pattern_value ::= character_expression
escape_value ::= character_expression
Все три операнда ( source_value, pattern_value и escape_value ) должны иметь true в том и только в том случае, когда шаблон ( pattern_value ) должным образом сопоставляется с исходной строкой ( source_value ).
Основное отличие предиката от рассмотренного ранее предиката состоит в существенно расширенных возможностях задания шаблона, основанных на использовании правил построения регулярных выражений. similar определяются следующими синтаксическими правилами:
regular_expression ::= regular_term
| regular_expression vertical_bar regular_term
regular_term ::= regular_factor
| regular_term regular_factor
regular_factor ::= regular_primary
| regular_primary *
| regular_primary +
regular_primary ::= character_specifier
| %
| regular_character_set
| ( regular_expression )
character_specifier ::= non_escape_character
| escape_character
regular_character_set ::= _
| left_bracket
character_enumeration_list right_bracket
| left_bracket
^ character_enumeration_list right_bracket
| left_bracket : regular_charset_id : right_bracket
character_enumeration ::= character_specifier
| character_specifier - character_specifier
regular_charset_id ::= ALPHA | UPPER | LOWER
| DIGIT | ALNUM
Поскольку в синтаксических правилах | ", " [ " и " ] ", используемые нами в качестве , являются vertical_bar, left_bracket и right_bracket соответственно.
Создаваемое по приведенным правилам регулярное выражение представляет собой символьную строку, содержащую все символы, которые требуется явно сопоставлять с символами строки-источника. В строке могут находиться специальные символы, представляющие собой заменители обычных символов (" % " и " _ "), обозначения операций (" | "), показатели числа возможных повторений (" * " и " + ") и т. д. При вычислении регулярного выражения образуются все возможные является true в том и только в том случае, когда среди всех pattern_value, найдется source_value.
Рассмотрим несколько примеров
Выражение '(This is string1)|(This is string2)' производит две '(This is string1)' и '(This is string2)'. В общем случае в круглых скобках могут находиться произвольные rexp1 и rexp2. Результатом вычисления '(rexp1)|(rexp2)' является множество rexp1, объединенное с множеством rexp2.
Выражение 'This is string [12]*' генерирует 'This is string ', 'This is string 1', 'This is string 2', 'This is string 11', 'This is string 22', 'This is string 12', 'This is string 21', 'This is string 111' и т. д. Конструкция в квадратных скобках представляет собой один из вариантов определения regular_character_set ). В данном случае символы, входящие в определяемый набор, просто перечисляются. При вычислении регулярного выражения в каждой из генерируемых
Специальный символ " * ", стоящий после закрывающей квадратной скобки, является показателем числа повторений. "Звездочка" означает, что в генерируемых символьных строках элемент регулярного выражения, непосредственно предшествующий "звездочке", может появляться ноль или более раз. Использование в такой же ситуации специального символа " + " означает, что в генерируемых символьных строках элемент регулярного выражения, непосредственно предшествующий символу "плюс", может появляться один или более раз.
Другая форма определения 'This is string [:. В этом случае конструкция в квадратных скобках представляет любой одиночный символ, изображающий десятичную цифру. Другими допустимыми в SQL ALPHA (любой (любой символ верхнего регистра), LOWER (любой символ нижнего регистра) и ALNUM (любой алфавитно-цифровой символ).
Определяемый 'This is string [3-8]' конструкция в квадратных скобках представляет собой любой одиночный символ, изображающий цифры от 3 до 8 включительно. Заметим, что при задании диапазона можно использовать любые символы, но требуется, чтобы значение
Наконец, имеется еще одна возможность определения '_S[^t]* генерирует все S ", за которым (не обязательно непосредственно) следует подстрока " ", но между " S " и " " отсутствуют вхождения символа " t ".
Как и в предикате , символ, определенный в разделе , поставленный перед любым специальным символом, отменяет специальную интерпретацию этого символа.
В заключение данного пункта вернемся к отложенному в разделе " . Напомним, что вызов этой функции определяется следующим синтаксисом:
SUBSTRING (character_value_expression
SIMILAR character_value_expression
ESCAPE character_value_expression)
Предположим, что в разделе (который должен присутствовать обязательно) задан символ " x ". Тогда 'rexp1x"rexp2x"rexp3', где rexp1, rexp2 и rexp3 являются регулярными выражениями. Функция пытается разделить символьную строку первого операнда на три раздела, первый из которых определяется путем сопоставления начала строки со строками, генерируемыми rexp1, второй - путем сопоставления оставшейся части строки первого операнда с rexp2 и третий - путем сопоставления конца этой строки с rexp3. Возвращаемым значением функции является средняя часть символьной строки первого операнда.
Вот пример вызова функции:
SUBSTRING ( 'This is string22'
SIMILAR 'This is\"[:ALPHA:]+\"[:DIGIT:]+'
ESCAPE '\' )
Результатом будет строка 'string'.
SELECT DEPT_NAME, DEPT_NO
FROM DEPT
WHERE DEPT_NAME SIMILAR TO
'(HARD|SOFT)WARE%\_[:DIGIT:]+' ESCAPE '\';
SELECT DEPT_NAME, DEPT_NO FROM DEPT WHERE DEPT_NAME SIMILAR TO '[^1-9]+%';
Предикат определяется следующим синтаксическим правилом:
exists_predicate ::= EXISTS (query_expression)
Значением условия EXISTS (query_expression) является true в том и только в том случае, когда мощность таблицы-результата выражения запросов больше нуля, иначе значением условия является false.
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT EMP.EMP_NO
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND EXISTS
(SELECT PRO.PRO_MNG
FROM PRO
WHERE PRO.PRO_MNG = EMP.EMP_NO));
Эту формулировку можно упростить, избавившись от самого вложенного запроса ():
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT EMP.EMP_NO
FROM EMP, PRO
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO);
Далее заметим, что по смыслу ):
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT *
FROM EMP, DEPT
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO);
Запросы с предикатом ):
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE (SELECT COUNT(*)
FROM EMP, DEPT
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO ) >= 1;
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE NOT EXISTS
(SELECT *
FROM EMP EMP1, EMP EMP2
WHERE EMP1.EMP_NO = DEPT.DEPT_MNG AND
EMP2.DEPT_NO = DEPT.DEPT_NO AND
EMP2.EMP_SAL > EMP1.EMP_SAL);
Этот
unique_predicate ::= UNIQUE (query_expression)
Результатом вычисления условия является true в том и только в том случае, когда в таблице-результате выражения запросов отсутствуют какие-либо две строки, одна из которых является дубликатом другой. В противном случае значение условия есть false.
SELECT DEPT_NO
FROM DEPT
WHERE UNIQUE
(SELECT EMP_NAME, EMP_BDATE
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO);
Возможна альтернативная, но более сложная формулировка этого запроса с использованием предиката ):
SELECT DEPT_NO
FROM DEPT
WHERE NOT EXISTS
(SELECT *
FROM EMP, EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO
AND EMP.DEPT_NO = DEPT.DEPT_NO
AND EMP1.DEPT_NO = DEPT.DEPT_NO
AND EMP1.EMP_NAME = EMP.EMP_NAME
AND(EMP1.EMP_BDATE = EMP.EMP_BDATE
OR (EMP.EMP_BDATE IS NULL
AND EMP1.EMP_BDATE IS NULL)));
Если же ограничиться требованием уникальности имен служащих, то возможна следующая формулировка ():
SELECT DEPT_NO
FROM DEPT
WHERE (SELECT COUNT (EMP_NAME)
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO) =
(SELECT COUNT (DISTINCT EMP_NAME)
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO);
Этот
overlaps_predicate ::= row_value_constructor OVERLAPS
row_value_constructor
Степень каждой из строк-операндов должна быть равна 2. Тип данных первого столбца каждого из операндов должен быть типом даты-времени, и типы данных первых столбцов должны быть
Пусть D1 и D2 - значения первого столбца первого и второго операндов соответственно. Если второй столбец первого операнда имеет E1 обозначает его значение. Если второй столбец первого операнда имеет тип , то пусть I1 -его значение, а E1 = D1 + I1. Если D1 является неопределенным значением или если E1 < D1, то пусть S1 = E1 и T1 = D1. В противном случае, пусть S1 = D1 и T1 = E1. Аналогично определяются S2 и T2 применительно ко второму операнду. Результат условия совпадает с результатом вычисления следующего
(S1 > S2 AND NOT (S1 >= T2 AND T1 >= T2)) OR (S2 > S1 AND NOT (S2 >= T1 AND T2 >= T1)) OR (S1 = S2 AND (T1 <> T2 OR T1 = T2))
SELECT PRO_NO
FROM PRO
WHERE (PRO_SDATE, PRO_DURAT) OVERLAPS
(DATE '2000-01-15', DATE '2002-12-31');
SELECT PRO_TITLE FROM PRO WHERE (PRO_SDATE, PRO_DURAT) OVERLAPS (CURRENT_DATE, INTERVAL '1' YEAR);
Этот
quantified_comparison_predicate ::= row_value_constructor
comp_op { ALL | SOME | ANY } query_expression
Степень первого операнда должна быть такой же, как и степень таблицы-результата выражения запросов. Типы данных значений строки-операнда должны быть совместимы с типами данных соответствующих столбцов выражения запроса. Сравнение строк производится по тем же правилам, что и для
Обозначим через x строку-первый операнд, а через S - результат s обозначает произвольную строку таблицы S. Тогда:
x comp_op ALL S имеет значение true в том и только в том случае, когда S пусто, или значение условия x comp_op s равно true для каждой строки s, входящей в S. Условие x comp_op ALL S имеет значение false в том и только в том случае, когда значение x comp_op s равно false хотя бы для одной строки s, входящей в S. В остальных случаях значение условия x comp_op ALL S равно unknown ;x comp_op SOME S имеет значение false в том и только в том случае, когда S пусто, или значение условия x comp_op s равно false для каждой строки s, входящей в S. Условие x comp_op SOME S имеет значение true в том и только в том случае, когда значение x comp_op s равно true хотя бы для одной строки s, входящей в S. В остальных случаях значение условия x comp_op SOME S равно unknown ;x comp_op ANY S эквивалентно условию x comp_op SOME S.SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL > SOME (SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
Одна из возможных альтернативных формулировок этого запроса может основываться на использовании предиката ):
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EXISTS(SELECT *
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_SAL > EMP1.EMP_SAL);
Вот альтернативная формулировка этого запроса, основанная на использовании
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_SAL > (SELECT MIN(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_NAME = SOME (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO);
Заметим, что эта формулировка эквивалентна следующей формулировке ():
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_NAME IN (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO);
Возможна формулировка с использованием
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
(SELECT COUNT(*)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO ) >= 1;
Наиболее лаконичным образом этот запрос можно сформулировать с использованием соединения ():
SELECT DISTINCT EMP.EMP_NO, EMP.EMP_NAME
FROM EMP, EMP EMP1
WHERE EMP.DEPT_NO = 65
AND EMP.EMP_NAME = EMP1.EMP_NAME
AND EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO;
В последней формулировке мы вынуждены везде использовать уточненные имена столбцов, потому что на одном уровне используются два вхождения таблицы .
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL >= ALL(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
Одна из возможных альтернативных формулировок этого запроса может основываться на использовании предиката ):
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND NOT EXISTS (SELECT *
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_SAL < EMP1.EMP_SAL);
Можно сформулировать этот же запрос с использованием
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL = (SELECT MAX(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE EMP_NAME <> ALL (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос можно переформулировать на основе использования предиката NOT EXISTS или COUNT (по причине очевидности мы не приводим эти формулировки), но, в отличие от случая в примере 14.22.3, формулировка в виде запроса с соединением здесь не проходит. Формулировка запроса
SELECT DISTINCT EMP_NO, EMP_NAME
FROM EMP, EMP EMP1
WHERE EMP.EMP_NAME <> EMP1.EMP_NAME
AND EMP1.EMP_NO <> EMP.EMP_NO);
эквивалентна формулировке
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE EMP_NAME <> SOME (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Очевидно, что этот запрос является бессмысленным ("Найти служащих, для которых имеется хотя бы один не однофамилец").
match_predicate ::= row_value_constructor
MATCH [ UNIQUE ] [ SIMPLE | PARTIAL | FULL ]
query_expression
Степень первого операнда должна совпадать со степенью таблицы-результата выражения запроса.
Пусть x обозначает строку-первый операнд. Тогда:
SIMPLE , то:x является неопределенным, то значением условия является true ;x нет неопределенных значений, то:UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s в такая, что x = s, то значением условия является true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что x = s, то значением условия является true ;false.PARTIAL, то:x являются неопределенными, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s, такая, что каждое отличное от неопределенного значение x равно соответствующему значению s, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что каждое отличное от неопределенного значение x равно соответствующему значению s, то значение условия есть true ;false.FULL, то:x неопределенные, то значение условия есть true ;x не является неопределенным, то:UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s, такая, что x = s, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что x = s, то значение условия есть true ;false.false.Все примеры этого пункта основаны на запросе "Найти номера служащих и номера их отделов для служащих, для которых в отделе со "схожим" номером работает служащий со "схожей" датой рождения" c некоторыми уточнениями.
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH SIMPLE
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, про которых:
Если использовать предикат , то мы получим данные о служащих, про которых:
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH PARTIAL
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, про которых:
Если использовать предикат , то мы получим данные о служащих, про которых:
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH UNIQUE FULL
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, о которых:
Если использовать предикат , то мы получим данные о служащих, о которых:
distinct_predicate ::= row_value_constructor IS DISTINCT FROM
row_value_constructor
Строки-операнды должны быть одинаковой степени. Типы данных соответствующих значений строк-операндов должны быть совместимы.
Напомним, что две строки s1 с именами столбцов c1, c2, …, cn и s2 с именами столбцов d1, d2, …, dn считаются строками-дубликатами, если для каждого i ( i = 1, 2, …, n ) либо ci и di не содержат NULL, и (ci = di) = true, либо и ci, и di содержат NULL. Значением условия s1 IS является true в том и только в том случае, когда строки s1 и s2 не являются дубликатами. В противном случае значением условия является false.
Заметим, что отрицательная форма условия - IS NOT - в стандарте SQL не поддерживается. Вместо этого можно воспользоваться выражением NOT s1 IS .
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65
AND (EMP_NAME, EMP_BDATE) IS DISTINCT FROM
(SELECT EMP1.EMP_NAME, EMP1.EMP_BDATE
FROM EMP EMP1, DEPT
WHERE EMP1.DEPT_NO = EMP.DEPT_NO
AND DEPT.DEPT_MNG = EMP1.EMP_NO);
SELECT EMP1.EMP_NO, EMP2.EMP_NO
FROM EMP EMP1, EMP EMP2
WHERE DEPT_NO = 65 AND EMP1.EMP_NO <> EMP2.EMP_NO
AND NOT ((EMP1.EMP_NAME, EMP1.EMP_BDATE) IS DISTINCT FROM
(EMP2.EMP_NAME, EMP2.EMP_BDATE));
В этой лекции мы обсудили наиболее важные возможности языка SQL, связанные с выборкой данных. Даже простые примеры, приводившиеся в лекции, показывают исключительную избыточность языка SQL. Еще в то время, когда действующим стандартом языка был SQL/92, была опубликована любопытная статья, в которой приводилось 25 формулировок одного и того же несложного запроса. При использовании всех возможностей SQL:1999 этих формулировок было бы гораздо больше.
Можно спорить, хорошо или плохо иметь возможность формулировать один и тот же запрос десятками разных способов. На мой взгляд, это не очень хорошо, поскольку увеличивает вероятность появления ошибок в запросах (особенно в сложных запросах). С другой стороны, таково объективное состояние дел, и мы стремились обеспечить в этой лекции материал, достаточный для того, чтобы прочувствовать различные возможности формулировки запросов. Как показывают следующие две лекции, возможности, предоставляемые оператором SELECT, в действительности гораздо шире.
Конструкции оператора SELECT языка SQL в значительной степени ортогональны. В частности, выбор способа указания ссылки на таблицы в разделе FROM никак не влияет на выбор варианта формирования условия выборки в разделе WHERE. Это полезное свойство языка позволяет нам абстрагироваться от обсуждавшегося в предыдущей лекции многообразия способов указания ссылки на таблицу и сосредоточиться на возможностях формирования запросов при использовании различных
В ); что одно SUBMULTISET ) и что IS A SET ). В этом курсе мы не приводим подробного описания этих видов
В лекции содержится много примеров запросов с использованием различных видов
Синтаксически
predicate ::= comparison_predicate
| between_predicate
| null_predicate
| in_predicate
| like_predicate
| similar_predicate
| exists_predicate
| unique_predicate
| overlaps_predicate
| quantified_comparison_predicate
| match_predicate
| distinct_predicate
Далее мы будем последовательно обсуждать разные виды СЛУЖАЩИЕ-ОТДЕЛЫ-ПРОЕКТЫ, определения таблиц которой на языке SQL были приведены в лекции 2. Для удобства повторим здесь структуру таблиц.
EMP_NO : EMP_NO |
EMP_NAME : VARCHAR |
EMP_BDATE : DATE |
EMP_SAL : SALARY |
DEPT_NO : DEPT_NO |
PRO_NO : PRO_NO |
DEPT_NO : DEPT_NO |
DEPT_NAME : VARCHAR |
DEPT_EMP_NO : INTEGER |
DEPT_TOTAL_SAL : SALARY |
DEPT_MNG : EMP_NO |
PRO_NO : PRO_NO |
PRO_TITLE : VARCHAR |
PRO_SDATE : DATEP |
PRO_DURAT : |
PRO_MNG : EMP_NO |
PRO_DESC : |
Столбцы EMP_NO, DEPT_NO и PRO_NO являются , DEPT и соответственно. Столбцы DEPT_NO и PRO_NO таблицы являются внешними ключами, ссылающимися на таблицы DEPT и соответственно ( DEPT_NO указывает на отделы, в которых работают служащие, а PRO_NO - на проекты, в которых они участвуют; оба столбца могут принимать неопределенные значения). Столбец DEPT_MNG является DEPT ( DEPT_MNG указывает на служащих, которые исполняют обязанности руководителей отделов; у отдела может не быть руководителя, и один служащий не может быть руководителем двух или более отделов). Столбец PRO_MNG является ( PRO_MNG указывает
на служащих, которые являются
Этот
comparison_predicate ::=
row_value_constructor comp_op row_value_constructor
comp_op ::= = | <> ("неравно")| < | >
| <= "меньше или равно"| >= "больше или равно"
Строки, являющиеся операндами операции сравнения, должны быть одинаковой степени. Типы данных соответствующих значений строк-операндов должны быть совместимы.
Пусть X и Y обозначают соответствующие элементы строк-операндов, а xv и yv - их значения. Тогда:
xv и/или yv являются неопределенными значениями, то значение условия X comp_op Y -unknown ;X comp_op Y является true или false в соответствии с естественными правилами применения операции сравнения.При этом:
X не равна длине строки Y, то для выравнивания длин строк более короткая строка расширяется символами набивки ( pad symbol ); если для используемого X и Y основано на сравнении соответствующих бит. Если Xi и Yi - значения i -тых бит X и Y соответственно и если lx и ly обозначает длину в битах X и Y соответственно, то:X равно Y тогда и только тогда, когда lx = ly и Xi = Yi для всех i ;X меньше Y тогда и только тогда, когда (a) lx < ly и Xi = Yi для всех i меньших или равных lx, или (b) Xi = Yi для всех i < n и Xn = 0, а Yn =1 для некоторого n меньшего или равного min (lx, ly).X и Y - сравниваемые значения, а H - наименее значимое поле даты-времени X и Y. Результат сравнения X comp_op Y определяется как (X - Y) H comp_ op INTERVAL (0) H. (Два значения Rx и Ry обозначают строки-операнды, а Rxi и Ryi - i -тые элементы Rx и Ry соответственно. Вот как определяется результат сравнения Rx comp_op Ry:Rx = Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i ;Rx <> Ry есть true тогда и только тогда, когда Rxi <> Ryi есть true для некоторого i ;Rx < Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i < n, и Rxn < Ryn есть true для некоторого n ;Rx > Ry есть true тогда и только тогда, когда Rxi = Ryi есть true для всех i < n, и Rxn > Ryn есть true для некоторого n ;Rx <= Ry есть true тогда и только тогда, когда Rx = Ry есть true или Rx < Ry есть true ;Rx >= Ry есть true тогда и только тогда, когда Rx = Ry есть true или Rx > Ry есть true ;Rx = Ry есть false тогда и только тогда, когда Rx <> Ry есть true ;Rx <> Ry есть false тогда и только тогда, когда Rx = Ry есть true ;Rx < Ry есть false тогда и только тогда, когда Rx >= Ry есть true ;Rx > Ry есть false тогда и только тогда, когда Rx <= Ry есть true ;Rx <= Ry есть false тогда и только тогда, когда Rx > Ry есть true ;Rx >= Ry есть false тогда и только тогда, когда Rx < Ry есть true ;Rx comp_op Ry есть unknown тогда и только тогда, когда Rx comp_op Ry не есть true или false.SELECT DISTINCT EMP.DEPT_NO FROM EMP WHERE EMP.EMP_NAME = 'Smith';
Мы добавили спецификацию ):
SELECT EMP.DEPT_NO, COUNT(*) FROM EMP WHERE EMP.NAME = 'Smith' GROUP BY EMP.DEPT_NO;
В этом варианте запроса спецификация не требуется, поскольку в запросе содержится раздел GROUP BY, группировка производится в соответствии со значениями столбца , и строка результата соответствует одной группе.
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO FROM EMP WHERE EMP.EMP_BDATE > DATE '1965-04-15';
В результате этого запроса дубликатов быть не может, поскольку в список выборки включен столбец, являющийся первичным ключом таблицы . Должно быть ясно, что по этой причине все строки результата будут различными.
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO
FROM EMP
WHERE EMP.EMP_SAL > 0.1 *
(SELECT DEPT_TOTAL_SAL
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO);
В этом WHERE этого DEPT. Во-вторых, в условии раздела WHERE , указанной в разделе FROM "внешнего" запроса. Подобные SELECT. В стандарте, естественно, не требуется, чтобы в
При выполнении внешнего запроса последовательно, строка за строкой, в некотором порядке, определяемом системой, производится проверка соответствия строк результирующей FROM условию раздела WHERE. Если это условие включает WHERE любого
Кстати, эквивалентная формулировка на языке SQL примера 14.3 выглядит следующим образом ():
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO FROM EMP, DEPT WHERE EMP.DEPT_NO = DEPT.DEPT_NO AND EMP.EMP_SAL > 0.1 * DEPT.TOTAL_SAL;
Мы видим, что ) эквисоединения таблиц и DEPT (по условию ). Подобную операцию часто называют
SELECT EMP1.EMP_NO, EMP1.EMP_NAME,
EMP1.DEPT_NO, EMP2.EMP_NAME
FROM EMP AS EMP1, EMP AS EMP2, DEPT
WHERE EMP1.EMP_SAL < 15000.00 AND
EMP1.DEPT_NO = DEPT.DEPT_NO AND
DEPT.DEPT_MNG = EMP2.EMP_NO;
Этот запрос представляет собой эквисоединение ограничения таблицы .
Покажем способ формулировки этого запроса с использованием вложенного
SELECT EMP.EMP_NO, EMP.EMP_NAME, EMP.DEPT_NO,
(SELECT EMP_NAME
FROM EMP
WHERE EMP_NO = DEPT_MNG)
FROM EMP, DEPT
WHERE EMP.EMP_SAL < 15000.00 AND
EMP.DEPT_NO = DEPT.DEPT_NO;
Как показывает последний пример, в условии выборки WHERE (если в запросе отсутствуют разделы GROUP BY и HAVING, случай (a)) или выполнения явно или неявно заданного раздела HAVING (случай (b)) выполняется раздел SELECT. При выполнении этого раздела на основе таблицы T1 в случае (a) или на основе сгруппированной таблицы T3 в случае (b) строится таблица T4, содержащая столько строк, сколько строк или групп строк содержится в таблицах T1 или T3 соответственно". В действительности, в общем случае очередная строка таблицы T4 должна строиться в тот момент, когда очередная строка или группа строк заносится в таблицу T1 или T3 соответственно.
between_predicate ::=
row_value_constructor [ NOT ] BETWEEN
row_value_constructor AND row_value_constructor
Все три строки-операнды должны иметь одну и ту же степень. Типы данных соответствующих значений строк-операндов должны быть совместимыми.
Пусть X, Y и Z обозначают первый, второй и третий операнды. Тогда по определению выражение X NOT BETWEEN Y AND Z NOT (X BETWEEN Y AND Z). Выражение X BETWEEN Y AND Z по определению эквивалентно X >= Y AND X <= Z.
SELECT EMP_NO, EMP_NAME, EMP_SAL FROM EMP WHERE EMP_SAL BETWEEN 12000.00 AND 15000.00;
SELECT EMP_NO, EMP_NAME, EMP_SAL
FROM EMP
WHERE EMP_SAL BETWEEN
(SELECT AVG(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO)
AND
(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP1.EMP_NO =
(SELECT DEPT.DEPT_MNG
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO));
В этом запросе можно выделить три интересных момента. Во-первых, диапазон значений предиката задан двумя подзапросами, результатом каждого из которых является единственное значение. Первый ) и отсутствует раздел GROUP BY, а второй - потому что в его разделе WHERE присутствует условие, задающее единственное значение получает EMP1 (в формулировке этого запроса мы старались использовать как можно меньше вспомогательных идентификаторов). Поскольку WHERE
используется ссылка на столбец таблицы из самого внешнего раздела FROM.
Предикат позволяет проверить, являются ли неопределенными значения всех элементов строки-операнда:
null_predicate ::= row_value_constructor IS [ NOT ] NULL
Пусть X обозначает строку-операнд. Если значения всех элементов X являются неопределенными, то значением условия X IS NULL является true ; иначе - false. Если ни у одного элемента X значение не является неопределенным, то значением условия X IS NOT NULL является true ; иначе - false.
Замечание: условие X IS NOT NULL имеет то же значение, что условие NOT X IS NULL для любого X в том и только в том случае, когда степень X равна 1. Полная null приведена в таблице 14.1.
| Вид операнда | Вид условия | |||
|---|---|---|---|---|
X IS X NULL |
IS NOT NULL |
NOT X IS NULL |
NOT X IS NOT NULL |
|
Степень 1: значение NULL |
true |
false |
false |
true |
Степень 1: значение отлично от NULL |
false |
true |
true |
false |
Степень > 1: у всех элементов значение NULL |
true |
false |
false |
true |
Степень > 1: у некоторых(не у всех) элементов значение NULL |
false |
false |
true |
true |
Степень > 1: ни у одного элемента нет значения NULL |
false |
true |
true |
false |
На самом деле, в нашей формулировке запроса из примера 14.6 есть одна неточность. Если у некоторого служащего номер отдела неизвестен (значение столбца у соответствующей строки таблицы служащих является неопределенным), то бессмысленно вычислять средний размер зарплаты отдела этого служащего и находить размер зарплаты руководителя отдела. Формулировка из примера 14.6 приведет к правильному результату, но это s - текущая строка таблицы , просматриваемой в цикле внешнего запроса, и пусть s.DEPT_NO содержит неопределенное значение. Тогда для строки s условие первого NULL = EMP1.DEPT_NO, и значением этого условия будет unknown для любой строки таблицы ( EMP1 ),
просматриваемой в цикле этого unknown не является разрешающим условием, результирующая таблица выдаст значение NULL. По этому поводу значением условия внешнего запроса будет unknown, и строка s не войдет в результирующую таблицу.IS NOT NULL и переписать запрос следующим образом:
SELECT EMP_NO, EMP_NAME, EMP_SAL
FROM EMP
WHERE DEPT_NO IS NOT NULL AND
EMP_SAL BETWEEN
(SELECT AVG(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO)
AND
(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP1.EMP_NO =
( SELECT DEPT.DEPT_MNG
FROM DEPT
WHERE DEPT.DEPT_NO = EMP.DEPT_NO ) );
SELECT EMP_NO, EMP_NAME FROM EMP WHERE DEPT_NO IS NULL;
in_predicate ::= row_value_constructor [ NOT ]
IN in_predicate_value
in_predicate_value ::= table_subquery
| (value_expression_comma_list)
Строка, являющаяся первым операндом, и таблица-второй операнд должны быть одинаковой степени. В частности, если второй операнд представляет собой список значений, то первый операнд должен иметь степень 1. Типы данных соответствующих столбцов операндов должны быть совместимы.
Пусть X обозначает строку-первый операнд, а S - множество строк второго операнда. Обозначим через s строку-элемент этого множества. Тогда по определению условие X IN S эквивалентно ORsS (X = s). Другими словами, X IN S принимает значение true в том и только в том случае, когда во множестве S существует хотя бы один элемент s, такой, что значением X = s является true. X IN S принимает значение false в том и только том случае, когда для всех элементов s множества S значением операции сравнения X = s является false. Иначе значением условия X IN S является unknown. Заметим, что для пустого множества S значением X IN S является false.
По определению условие X NOT IN S эквивалентно NOT (X IN S).
SELECT EMP_NO, EMP_NAME, DEPT_NO FROM EMP WHERE DEPT_NO IN (15, 17, 19);
Конечно, эта формулировка запроса эквивалентна следующей формулировке ():
SELECT EMP_NO, EMP_NAME, DEPT_NO FROM EMP WHERE DEPT_NO = 15 OR DEPT_NO = 17 OR DEPT_NO = 19;
SELECT EMP_NO
FROM EMP
WHERE EMP_NO NOT IN (SELECT DEPT_MNG FROM DEPT)
AND EMP_SAL IN (SELECT EMP_SAL FROM EMP,
DEPT WHERE EMP_NO = DEPT_MNG);
Запросы, содержащие предикат ):
SELECT DISTINCT EMP_NO FROM EMP, EMP EMP1, DEPT WHERE EMP_NO NOT IN (SELECT DEPT_MNG FROM DEPT) AND EMP_SAL = EMP1_SAL AND EMP1.EMP_NO = DEPT.DEPT_MNG;
По поводу этой второй формулировки следует сделать два замечания. Во-первых, как видно, мы изменили только ту часть условия, в которой использовался предикат , и не затронули предикат NOT IN. Запросы с предикатами NOT IN запросами с соединениями так просто не заменяются. Во-вторых, в разделе SELECT было добавлено ключевое слово , потому что в результате запроса во второй формулировке для каждого служащего будет содержаться столько строк, сколько существует руководителей отделов, получающих такую же зарплату, что и данный служащий.
Формально предикат определяется следующими синтаксическими правилами:
like_predicate ::= source_value [ NOT ]
LIKE pattern_value [ ESCAPE escape_value ]
source_value ::= value_expression
pattern_value ::= value_expression
escape_value ::= value_expression
Все три операнда ( source_value, pattern_value и escape_value ) должны быть одного типа: либо . Битовые строки типов BIT и BIT не допускаются.true в том и только в том случае, когда исходная строка ( source_value ) может быть сопоставлена с заданным шаблоном ( pattern_value ).
Если обрабатываются условия отсутствует, то при сопоставлении шаблона со строкой производится специальная интерпретация двух символов шаблона: символ подчеркивания (' _ ') обозначает любой одиночный символ; символ процента (' % ') обозначает последовательность произвольных символов произвольной длины (длина последовательности может быть нулевой). Если же раздел присутствует и специфицирует некоторый одиночный символ x, то пары символов " x_ " и " x% " представляют одиночные символы " _ " и " % " соответственно.
В случае обработки битовых строк сопоставление шаблона со строкой производится восьмерками соседних бит ( октетами ). В соответствии со X'25' и X'5F' (X'25' и X'5F'.
Значение предиката есть unknown, если значение первого или второго операндов является неопределенным. Условие x NOT LIKE y эквивалентно условию NOT x LIKE y .
SELECT PRO_TITLE
FROM PRO
WHERE PRO_TITLE LIKE '%next%step%'
OR PRO_TITLE LIKE 'Next%step%';
Это очень неудачный запрос, потому что его выполнение, скорее всего, вынудит СУБД просмотреть все строки таблицы ):
SELECT PRO_TITLE FROM PRO WHERE PRO_TITLE LIKE '%ext%step%';
SELECT DISTINCT DEPT.DEPT_NO FROM EMP, DEPT, PRO WHERE EMP.EMP_NO = PRO.PRO_MNG AND EMP.DEPT_NO = DEPT.DEPT_NO AND PRO.PRO_TITLE LIKE DEPT.DEPT_NAME || '%';
Вот как может выглядеть формулировка этого запроса, если использовать вложенные
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE DEPT.DEPT_NO IN
(SELECT EMP.DEPT_NO
FROM EMP
WHERE EMP.EMP_NO IN
(SELECT PRO.PRO_MNG FROM PRO
WHERE PRO.PRO_TITLE LIKE DEPT.DEPT_NAME || '%'));
SELECT DEPT_NO FROM DEPT WHERE DEPT_NAME NOT LIKE 'Software%';
Формально предикат определяется следующими синтаксическими правилами:
similar_predicate ::= source_value [ NOT ]
SIMILAR TO pattern_value [ ESCAPE escape_value ]
source_value ::= character_expression
pattern_value ::= character_expression
escape_value ::= character_expression
Все три операнда ( source_value, pattern_value и escape_value ) должны иметь true в том и только в том случае, когда шаблон ( pattern_value ) должным образом сопоставляется с исходной строкой ( source_value ).
Основное отличие предиката от рассмотренного ранее предиката состоит в существенно расширенных возможностях задания шаблона, основанных на использовании правил построения регулярных выражений. similar определяются следующими синтаксическими правилами:
regular_expression ::= regular_term
| regular_expression vertical_bar regular_term
regular_term ::= regular_factor
| regular_term regular_factor
regular_factor ::= regular_primary
| regular_primary *
| regular_primary +
regular_primary ::= character_specifier
| %
| regular_character_set
| ( regular_expression )
character_specifier ::= non_escape_character
| escape_character
regular_character_set ::= _
| left_bracket
character_enumeration_list right_bracket
| left_bracket
^ character_enumeration_list right_bracket
| left_bracket : regular_charset_id : right_bracket
character_enumeration ::= character_specifier
| character_specifier - character_specifier
regular_charset_id ::= ALPHA | UPPER | LOWER
| DIGIT | ALNUM
Поскольку в синтаксических правилах | ", " [ " и " ] ", используемые нами в качестве , являются vertical_bar, left_bracket и right_bracket соответственно.
Создаваемое по приведенным правилам регулярное выражение представляет собой символьную строку, содержащую все символы, которые требуется явно сопоставлять с символами строки-источника. В строке могут находиться специальные символы, представляющие собой заменители обычных символов (" % " и " _ "), обозначения операций (" | "), показатели числа возможных повторений (" * " и " + ") и т. д. При вычислении регулярного выражения образуются все возможные является true в том и только в том случае, когда среди всех pattern_value, найдется source_value.
Рассмотрим несколько примеров
Выражение '(This is string1)|(This is string2)' производит две '(This is string1)' и '(This is string2)'. В общем случае в круглых скобках могут находиться произвольные rexp1 и rexp2. Результатом вычисления '(rexp1)|(rexp2)' является множество rexp1, объединенное с множеством rexp2.
Выражение 'This is string [12]*' генерирует 'This is string ', 'This is string 1', 'This is string 2', 'This is string 11', 'This is string 22', 'This is string 12', 'This is string 21', 'This is string 111' и т. д. Конструкция в квадратных скобках представляет собой один из вариантов определения regular_character_set ). В данном случае символы, входящие в определяемый набор, просто перечисляются. При вычислении регулярного выражения в каждой из генерируемых
Специальный символ " * ", стоящий после закрывающей квадратной скобки, является показателем числа повторений. "Звездочка" означает, что в генерируемых символьных строках элемент регулярного выражения, непосредственно предшествующий "звездочке", может появляться ноль или более раз. Использование в такой же ситуации специального символа " + " означает, что в генерируемых символьных строках элемент регулярного выражения, непосредственно предшествующий символу "плюс", может появляться один или более раз.
Другая форма определения 'This is string [:. В этом случае конструкция в квадратных скобках представляет любой одиночный символ, изображающий десятичную цифру. Другими допустимыми в SQL ALPHA (любой (любой символ верхнего регистра), LOWER (любой символ нижнего регистра) и ALNUM (любой алфавитно-цифровой символ).
Определяемый 'This is string [3-8]' конструкция в квадратных скобках представляет собой любой одиночный символ, изображающий цифры от 3 до 8 включительно. Заметим, что при задании диапазона можно использовать любые символы, но требуется, чтобы значение
Наконец, имеется еще одна возможность определения '_S[^t]* генерирует все S ", за которым (не обязательно непосредственно) следует подстрока " ", но между " S " и " " отсутствуют вхождения символа " t ".
Как и в предикате , символ, определенный в разделе , поставленный перед любым специальным символом, отменяет специальную интерпретацию этого символа.
В заключение данного пункта вернемся к отложенному в разделе " . Напомним, что вызов этой функции определяется следующим синтаксисом:
SUBSTRING (character_value_expression
SIMILAR character_value_expression
ESCAPE character_value_expression)
Предположим, что в разделе (который должен присутствовать обязательно) задан символ " x ". Тогда 'rexp1x"rexp2x"rexp3', где rexp1, rexp2 и rexp3 являются регулярными выражениями. Функция пытается разделить символьную строку первого операнда на три раздела, первый из которых определяется путем сопоставления начала строки со строками, генерируемыми rexp1, второй - путем сопоставления оставшейся части строки первого операнда с rexp2 и третий - путем сопоставления конца этой строки с rexp3. Возвращаемым значением функции является средняя часть символьной строки первого операнда.
Вот пример вызова функции:
SUBSTRING ( 'This is string22'
SIMILAR 'This is\"[:ALPHA:]+\"[:DIGIT:]+'
ESCAPE '\' )
Результатом будет строка 'string'.
SELECT DEPT_NAME, DEPT_NO
FROM DEPT
WHERE DEPT_NAME SIMILAR TO
'(HARD|SOFT)WARE%\_[:DIGIT:]+' ESCAPE '\';
SELECT DEPT_NAME, DEPT_NO FROM DEPT WHERE DEPT_NAME SIMILAR TO '[^1-9]+%';
Предикат определяется следующим синтаксическим правилом:
exists_predicate ::= EXISTS (query_expression)
Значением условия EXISTS (query_expression) является true в том и только в том случае, когда мощность таблицы-результата выражения запросов больше нуля, иначе значением условия является false.
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT EMP.EMP_NO
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND EXISTS
(SELECT PRO.PRO_MNG
FROM PRO
WHERE PRO.PRO_MNG = EMP.EMP_NO));
Эту формулировку можно упростить, избавившись от самого вложенного запроса ():
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT EMP.EMP_NO
FROM EMP, PRO
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO);
Далее заметим, что по смыслу ):
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE EXISTS
(SELECT *
FROM EMP, DEPT
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO);
Запросы с предикатом ):
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE (SELECT COUNT(*)
FROM EMP, DEPT
WHERE EMP.DEPT_NO = DEPT.DEPT_NO
AND PRO.PRO_MNG = EMP.EMP_NO ) >= 1;
SELECT DEPT.DEPT_NO
FROM DEPT
WHERE NOT EXISTS
(SELECT *
FROM EMP EMP1, EMP EMP2
WHERE EMP1.EMP_NO = DEPT.DEPT_MNG AND
EMP2.DEPT_NO = DEPT.DEPT_NO AND
EMP2.EMP_SAL > EMP1.EMP_SAL);
Этот
unique_predicate ::= UNIQUE (query_expression)
Результатом вычисления условия является true в том и только в том случае, когда в таблице-результате выражения запросов отсутствуют какие-либо две строки, одна из которых является дубликатом другой. В противном случае значение условия есть false.
SELECT DEPT_NO
FROM DEPT
WHERE UNIQUE
(SELECT EMP_NAME, EMP_BDATE
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO);
Возможна альтернативная, но более сложная формулировка этого запроса с использованием предиката ):
SELECT DEPT_NO
FROM DEPT
WHERE NOT EXISTS
(SELECT *
FROM EMP, EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO
AND EMP.DEPT_NO = DEPT.DEPT_NO
AND EMP1.DEPT_NO = DEPT.DEPT_NO
AND EMP1.EMP_NAME = EMP.EMP_NAME
AND(EMP1.EMP_BDATE = EMP.EMP_BDATE
OR (EMP.EMP_BDATE IS NULL
AND EMP1.EMP_BDATE IS NULL)));
Если же ограничиться требованием уникальности имен служащих, то возможна следующая формулировка ():
SELECT DEPT_NO
FROM DEPT
WHERE (SELECT COUNT (EMP_NAME)
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO) =
(SELECT COUNT (DISTINCT EMP_NAME)
FROM EMP
WHERE EMP.DEPT_NO = DEPT.DEPT_NO);
Этот
overlaps_predicate ::= row_value_constructor OVERLAPS
row_value_constructor
Степень каждой из строк-операндов должна быть равна 2. Тип данных первого столбца каждого из операндов должен быть типом даты-времени, и типы данных первых столбцов должны быть
Пусть D1 и D2 - значения первого столбца первого и второго операндов соответственно. Если второй столбец первого операнда имеет E1 обозначает его значение. Если второй столбец первого операнда имеет тип , то пусть I1 -его значение, а E1 = D1 + I1. Если D1 является неопределенным значением или если E1 < D1, то пусть S1 = E1 и T1 = D1. В противном случае, пусть S1 = D1 и T1 = E1. Аналогично определяются S2 и T2 применительно ко второму операнду. Результат условия совпадает с результатом вычисления следующего
(S1 > S2 AND NOT (S1 >= T2 AND T1 >= T2)) OR (S2 > S1 AND NOT (S2 >= T1 AND T2 >= T1)) OR (S1 = S2 AND (T1 <> T2 OR T1 = T2))
SELECT PRO_NO
FROM PRO
WHERE (PRO_SDATE, PRO_DURAT) OVERLAPS
(DATE '2000-01-15', DATE '2002-12-31');
SELECT PRO_TITLE FROM PRO WHERE (PRO_SDATE, PRO_DURAT) OVERLAPS (CURRENT_DATE, INTERVAL '1' YEAR);
Этот
quantified_comparison_predicate ::= row_value_constructor
comp_op { ALL | SOME | ANY } query_expression
Степень первого операнда должна быть такой же, как и степень таблицы-результата выражения запросов. Типы данных значений строки-операнда должны быть совместимы с типами данных соответствующих столбцов выражения запроса. Сравнение строк производится по тем же правилам, что и для
Обозначим через x строку-первый операнд, а через S - результат s обозначает произвольную строку таблицы S. Тогда:
x comp_op ALL S имеет значение true в том и только в том случае, когда S пусто, или значение условия x comp_op s равно true для каждой строки s, входящей в S. Условие x comp_op ALL S имеет значение false в том и только в том случае, когда значение x comp_op s равно false хотя бы для одной строки s, входящей в S. В остальных случаях значение условия x comp_op ALL S равно unknown ;x comp_op SOME S имеет значение false в том и только в том случае, когда S пусто, или значение условия x comp_op s равно false для каждой строки s, входящей в S. Условие x comp_op SOME S имеет значение true в том и только в том случае, когда значение x comp_op s равно true хотя бы для одной строки s, входящей в S. В остальных случаях значение условия x comp_op SOME S равно unknown ;x comp_op ANY S эквивалентно условию x comp_op SOME S.SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL > SOME (SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
Одна из возможных альтернативных формулировок этого запроса может основываться на использовании предиката ):
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EXISTS(SELECT *
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_SAL > EMP1.EMP_SAL);
Вот альтернативная формулировка этого запроса, основанная на использовании
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_SAL > (SELECT MIN(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_NAME = SOME (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO);
Заметим, что эта формулировка эквивалентна следующей формулировке ():
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
EMP_NAME IN (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO);
Возможна формулировка с использованием
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65 AND
(SELECT COUNT(*)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO ) >= 1;
Наиболее лаконичным образом этот запрос можно сформулировать с использованием соединения ():
SELECT DISTINCT EMP.EMP_NO, EMP.EMP_NAME
FROM EMP, EMP EMP1
WHERE EMP.DEPT_NO = 65
AND EMP.EMP_NAME = EMP1.EMP_NAME
AND EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_NO <> EMP1.EMP_NO;
В последней формулировке мы вынуждены везде использовать уточненные имена столбцов, потому что на одном уровне используются два вхождения таблицы .
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL >= ALL(SELECT EMP1.EMP_SAL
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
Одна из возможных альтернативных формулировок этого запроса может основываться на использовании предиката ):
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND NOT EXISTS (SELECT *
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO
AND EMP.EMP_SAL < EMP1.EMP_SAL);
Можно сформулировать этот же запрос с использованием
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL = (SELECT MAX(EMP1.EMP_SAL)
FROM EMP EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE EMP_NAME <> ALL (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос можно переформулировать на основе использования предиката NOT EXISTS или COUNT (по причине очевидности мы не приводим эти формулировки), но, в отличие от случая в примере 14.22.3, формулировка в виде запроса с соединением здесь не проходит. Формулировка запроса
SELECT DISTINCT EMP_NO, EMP_NAME
FROM EMP, EMP EMP1
WHERE EMP.EMP_NAME <> EMP1.EMP_NAME
AND EMP1.EMP_NO <> EMP.EMP_NO);
эквивалентна формулировке
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE EMP_NAME <> SOME (SELECT EMP1.EMP_NAME
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Очевидно, что этот запрос является бессмысленным ("Найти служащих, для которых имеется хотя бы один не однофамилец").
match_predicate ::= row_value_constructor
MATCH [ UNIQUE ] [ SIMPLE | PARTIAL | FULL ]
query_expression
Степень первого операнда должна совпадать со степенью таблицы-результата выражения запроса.
Пусть x обозначает строку-первый операнд. Тогда:
SIMPLE , то:x является неопределенным, то значением условия является true ;x нет неопределенных значений, то:UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s в такая, что x = s, то значением условия является true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что x = s, то значением условия является true ;false.PARTIAL, то:x являются неопределенными, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s, такая, что каждое отличное от неопределенного значение x равно соответствующему значению s, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что каждое отличное от неопределенного значение x равно соответствующему значению s, то значение условия есть true ;false.FULL, то:x неопределенные, то значение условия есть true ;x не является неопределенным, то:UNIQUE , и в результате выражения запроса существует (возможно, не уникальная) строка s, такая, что x = s, то значение условия есть true ;UNIQUE , и в результате выражения запроса существует уникальная строка s, такая, что x = s, то значение условия есть true ;false.false.Все примеры этого пункта основаны на запросе "Найти номера служащих и номера их отделов для служащих, для которых в отделе со "схожим" номером работает служащий со "схожей" датой рождения" c некоторыми уточнениями.
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH SIMPLE
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, про которых:
Если использовать предикат , то мы получим данные о служащих, про которых:
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH PARTIAL
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, про которых:
Если использовать предикат , то мы получим данные о служащих, про которых:
SELECT EMP_NO, DEPT_NO
FROM EMP
WHERE (DEPT_NO, EMP_BDATE) MATCH UNIQUE FULL
(SELECT EMP1.DEPT_NO, EMP1.EMP_BDATE
FROM EMP EMP1
WHERE EMP1.EMP_NO <> EMP.EMP_NO);
Этот запрос вернет данные о служащих, о которых:
Если использовать предикат , то мы получим данные о служащих, о которых:
distinct_predicate ::= row_value_constructor IS DISTINCT FROM
row_value_constructor
Строки-операнды должны быть одинаковой степени. Типы данных соответствующих значений строк-операндов должны быть совместимы.
Напомним, что две строки s1 с именами столбцов c1, c2, …, cn и s2 с именами столбцов d1, d2, …, dn считаются строками-дубликатами, если для каждого i ( i = 1, 2, …, n ) либо ci и di не содержат NULL, и (ci = di) = true, либо и ci, и di содержат NULL. Значением условия s1 IS является true в том и только в том случае, когда строки s1 и s2 не являются дубликатами. В противном случае значением условия является false.
Заметим, что отрицательная форма условия - IS NOT - в стандарте SQL не поддерживается. Вместо этого можно воспользоваться выражением NOT s1 IS .
SELECT EMP_NO, EMP_NAME
FROM EMP
WHERE DEPT_NO = 65
AND (EMP_NAME, EMP_BDATE) IS DISTINCT FROM
(SELECT EMP1.EMP_NAME, EMP1.EMP_BDATE
FROM EMP EMP1, DEPT
WHERE EMP1.DEPT_NO = EMP.DEPT_NO
AND DEPT.DEPT_MNG = EMP1.EMP_NO);
SELECT EMP1.EMP_NO, EMP2.EMP_NO
FROM EMP EMP1, EMP EMP2
WHERE DEPT_NO = 65 AND EMP1.EMP_NO <> EMP2.EMP_NO
AND NOT ((EMP1.EMP_NAME, EMP1.EMP_BDATE) IS DISTINCT FROM
(EMP2.EMP_NAME, EMP2.EMP_BDATE));
В этой лекции мы обсудили наиболее важные возможности языка SQL, связанные с выборкой данных. Даже простые примеры, приводившиеся в лекции, показывают исключительную избыточность языка SQL. Еще в то время, когда действующим стандартом языка был SQL/92, была опубликована любопытная статья, в которой приводилось 25 формулировок одного и того же несложного запроса. При использовании всех возможностей SQL:1999 этих формулировок было бы гораздо больше.
Можно спорить, хорошо или плохо иметь возможность формулировать один и тот же запрос десятками разных способов. На мой взгляд, это не очень хорошо, поскольку увеличивает вероятность появления ошибок в запросах (особенно в сложных запросах). С другой стороны, таково объективное состояние дел, и мы стремились обеспечить в этой лекции материал, достаточный для того, чтобы прочувствовать различные возможности формулировки запросов. Как показывают следующие две лекции, возможности, предоставляемые оператором SELECT, в действительности гораздо шире.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.