2008 г.
Базы данных. Вводный курс
Сергей Кузнецов
Назад Содержание Вперёд
17.3.3. Табличное выражение, спецификация запроса и выражение запросов
Табличным выражением (table_expression) называется конструкция
table_expression ::= FROM table_reference_commalist
[ WHERE conditional_expression ]
[ GROUP BY column_name_commalist ]
[ HAVING conditional_expression ]
Спецификацией запроса (query_specification) называется конструкция
query_specification SELECT [ ALL | DISTINCT ]
select_item_commalist table_expression
Наконец, выражением запросов (query_expression) называется конструкция
query_expression ::= [ with_clause ] query_expression_body
query_expression_body ::= { non_join_query_expression
| joined_table }
non_join_query_expression ::= non_join_query_term
| query_expression_body
{ UNION | EXCEPT }[ ALL | DISTINCT ]
[ corresponding_spec ] query_term
query_term ::= non_join_query_term | joined_table
non_join_query_term ::= non_join_query_primary
| query_term INTERSECT [ ALL | DISTINCT ]
[ corresponding_spec ] query_primary
query_primary ::= non_join_query_primary | joined_table
non_join_query_primary ::= simple_table
| (non_join_query_expression)
simple_table ::= query_specification
| table_value_constructor
| TABLE table_name
corresponding_spec ::= CORRESPONDING
[ BY column_name_comma_list ]
Если не обращать внимания на не обсуждавшиеся пока конструкции joined_table и table_value_constructor, синтаксические правила показывают, что выражение запросов строится из выражений, значениями которых являются таблицы, с использованием «теоретико-множественных»129) операций UNION (объединение), EXCEPT (вычитание) и INTERSECT (пересечение). Операция пересечения является «мультипликативной» и обладает более высоким приоритетом, чем «аддитивные» операции объединения и вычитания. Вычисление выражения производится слева направо с учетом приоритетов операций и круглых скобок. При этом действуют следующие правила.
- Если выражение запросов не включает ни одной теоретико-множественной операции, то результатом вычисления выражения запросов является результат вычисления простой или соединенной таблицы.
- Если в терме (
non_join_query_term) или выражении запросов (non_join_query_expression) без соединения присутствует теоретико-множественная операция, то пусть T1, T2 и TR обозначают соответственно первый операнд, второй операнд и результат терма или выражения соответственно, а OP – используемую теоретико-множественную операцию. - Если в операции присутствует спецификация
CORRESPONDING, то:
- если присутствует конструкция
BY column_name_comma_list, то все имена в этом списке должны быть различны, и каждое имя должно являться одновременно именем некоторого столбца таблицы T1 и именем некоторого столбца таблицы T2, причем типы этих столбцов должны быть совместимыми; обозначим данный список имен через SL; - если список соответствия столбцов не задан, пусть
SL обозначает список имен столбцов, являющихся именами столбцов и в T1, и в T2, в том порядке, в котором эти имена фигурируют в T1; - вычисляемые терм или выражение запросов без соединения эквивалентны выражению
(SELECT SL FROM T1) OP (SELECT SL FROM T2), не включающему спецификацию CORRESPONDING.
- При отсутствии в операции спецификации
CORRESPONDING операция выполняется таким образом, как если бы эта спецификация присутствовала и включала конструкцию BY column_name_comma_list, в которой были бы перечислены все столбцы таблицы T1.130) - При выполнении операции
OP две строки s1 с именами столбцов c1, c2, …, cn и s2 с именами столбцов d1, d2, …, dn считаются строками-дубликатами, если для каждого i (i = 1, 2, …, n) либо ci и di не содержат NULL, и (ci = di) = true131), либо и ci, и di содержат NULL. - Если в операции
OP не задана спецификация ALL, то в TR строки-дубликаты удаляются. - Если спецификация
ALL задана, то пусть s – строка, являющаяся дубликатом некоторой строки T1, или некоторой строки T2, или обеих; пусть m – число дубликатов s в T1, а n – число дубликатов s в T2. Тогда:
- если указана операция
UNION, то число дубликатов s в TR равно m + n; - если указана операция
EXCEPT, то число дубликатов s в TR равно max ((m-n),0); - если указана операция
INTERSECT, то число дубликатов s в TR равно min (m,n).
17.3.4. Раздел WITH выражения запросов
Как видно из синтаксиса выражения запросов, в этом выражении может присутствовать раздел WITH. Он задается в следующем синтаксисе:
with_clause ::= WITH [ RECURSIVE ] with_element_comma_list
with_element ::= query_name [ (column_name_list) ]
AS (query_expression) [ search_or_cycle_clause ]
Общую форму раздела WITH мы обсудим в лекции 20, когда будем рассматривать средства формулировки рекурсивных запросов. Пока ограничимся случаем, когда в разделе WITH отсутствуют спецификация RECURSIVE и search_or_cycle_clause. Тогда конструкция
WITH query_name (c1, c2, … cn) AS (query_exp_1) query_exp_2
означает, что в любом месте выражения запросов query_exp_2, где допускается появление ссылки на таблицу, можно использовать имя query_name. Можно считать, что перед выполнением query_exp_2 происходит выполнение query_exp_1, и результирующая таблица с именами столбцов c1, c2, … cn сохраняется под именем query_name. Как мы увидим позже, в этом случае раздел WITH фактически служит для локального определения представляемой таблицы (VIEW).
17.3.5. Конструкторы значения строки и таблицы
Чтобы завершить обсуждение выражений запросов (с учетом того, что конструкция соединенных таблиц (joined_table) отложена на лекцию 19), нам осталось рассмотреть конструкции table_value_constructor и TABLE table_name.
В определении конструктора значения-таблицы используется конструктор значения-строки, который строит упорядоченный набор скалярных значений, представляющий строку (возможно и использование подзапроса132)):
row_value_constructor ::= row_value_constructor_element
| [ ROW ] (row_value_constructor_element_comma_list)
| row_subquery
row_value_constructor_element ::= value_expression | NULL | DEFAULT
Заметим, что значение элемента по умолчанию можно использовать только в том случае, когда конструктор значения-строки применяется в операторе INSERT (тогда этим значением будет значение по умолчанию соответствующего столбца).
Конструктор значения-таблицы производит таблицу на основе заданного набора конструкторов значений-строк:
table_value_constructor ::= VALUES
row_value_constructor_comma_list
Конечно, для того чтобы корректно построить таблицу, требуется, чтобы строки, производимые всеми конструкторами строк, были одной и той же степени и чтобы типы (или домены) соответствующих столбцов являлись приводимыми.
Наконец, конструкция TABLE table_name является сокращенной формой записи выражения SELECT * FROM table_name.
17.3.6. Ссылки на базовые, представляемые и порождаемые таблицы
Теперь мы можем завершить обсуждение разновидностей ссылок на таблицу в разделе FROM. Для удобства повторим синтаксические правила (опустив конструкции, рассмотрение которых отложено на следующие лекции или выходит за пределы материала данного курса):
table_reference ::= table_primary
table_primary ::= table_or_query_name [ [ AS ] correlation_name
[ (derived_column_list) ] ]
| derived_table [ AS ] correlation_name
[ (derived_column_list) ]
table_or_query_name ::= { table_name | query_name }
derived_table ::= (query_expression)
Итак, в самом простом случае в качестве ссылки на таблицу используется имя таблицы (базовой или представляемой) или имя запроса, присоединенного к данному запросу с помощью раздела WITH. В другом случае (derived_table) порождаемая таблица задается выражением запроса, заключенным в круглые скобки. Явное указание имен столбцов результата запроса из раздела WITH или порождаемой таблицы требуется в том случае, когда эти имена не выводятся явно из соответствующего выражения запроса. Обратите внимание, что в таких случаях в соответствующем элементе списка раздела FROM должен указываться псевдоним (correlation_name), потому что иначе таблица была бы вообще лишена имени. Можно считать, что выражение запроса вычисляется и сохраняется во временной таблице при обработке раздела FROM.
Возможно, некоторых читателей смутила рекурсивная природа синтаксических определений, приведенных в этом подразделе. Чтобы определить понятие ссылки на таблицу в разделе FROM оператора выборки, который опирается на спецификацию запроса, нам пришлось ввести более общее понятие выражения запросов, в определении которого используется спецификация запроса. Да, действительно, многие синтаксические конструкции SQL определяются рекурсивно. Но эта рекурсия никогда не приводит к зацикливанию. В частности, раскрутка рекурсии операторов выборки основывается на базовой, не выделяемой отдельными синтаксическими правилами форме, в которой в разделе FROM указываются только имена базовых таблиц.
17.3.7. Представляемые таблицы, или представления (VIEW)
Еще одним примером рекурсивности спецификаций языка SQL является то, что в конце этой лекции мы вынуждены прервать обсуждение оператора выборки и ввести понятие представляемой таблицы, или представления, которую можно использовать в операторе выборки наряду с базовыми таблицами. Только после этого можно будет считать обсуждение ссылок на таблицы в разделе FROM условно завершенным. Итак, оператор создания представления в общем случае определяется следующими синтаксическими правилами:
create_view ::= CREATE [ RECURSIVE ] VIEW table_name
[ column_name_comma_list ]
AS query_expression
[ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
Рекурсивные представления (такие, в определении которых присутствует ключевое слово RECURSIVE) и необязательный раздел WITH CHECK OPTION мы обсудим в лекции 21 (пока лишь заметим, что этот раздел связан с особенностями выполнения операций обновления базы данных через представления). Здесь мы кратко рассмотрим только простую форму представлений, определяемых по следующим правилам:
create_view ::= CREATE VIEW table_name
[ column_name_comma_list ]
AS query_expression
Имя таблицы, задаваемое в определении представления, существует в том же пространстве имен, что и имена базовых таблиц, и, следовательно, должно отличаться от всех имен таблиц (базовых и представляемых), созданных тем же пользователем. Если имя представления встречается в разделе FROM какого-либо оператора выборки, то вычисляется выражение запроса, указанное в разделе AS, и оператор выборки работает с результирующей таблицей этого выражения запроса.133) Явное указание имен столбцов представляемой таблицы требуется в том случае, когда эти имена не выводятся из соответствующего выражения запроса.
Как и для всех других вариантов оператора CREATE, для CREATE VIEW имеется обратный оператор DROP VIEW table_name, выполнение которого приводит к отмене определения представления (реально это выражается в удалении данных о представлении из таблиц-каталогов базы данных). После выполнения операции пользоваться представлением с данным именем становится невозможно.134)
17.4. Заключение
В ходе чтения лекций, посвященных оператору SELECT языка SQL, мне неоднократно случалось слышать жалобы студентов на сухость начального материала и отсутствие иллюстрирующих примеров. Однако я не встречал ни одного учебного пособия по языку SQL, основанного на примерах (среди многочисленных изданий типа «SQL за 24 часа», «SQL для чайников» и даже «SQL для идиотов»), который действительно давал бы представление об SQL как языке, а не служил инструкцией армейского типа.
Сложность организации оператора выборки не позволяет сразу начинать с полноценных примеров, а для демонстрации примеров промежуточных конструкций требуется создание неприемлемо громоздкого контекста. Поэтому могу лишь принести извинения за некоторую сухость этой лекции.
С другой стороны, теперь мы уже вплотную подошли к тому этапу, на котором возможно использование иллюстраций, и в следующих лекциях их будет достаточно, хотя проиллюстрировать все интересные разновидности оператора SELECT все равно не представляется возможным, поскольку число вариантов близко к астрономическому.
129 Мы использовали кавычки, поскольку таблицы, к которым применяются операции, в общем случае могут содержать строки-дубликаты, т.е. являться мультимножествами.
130 Другими словами, при отсутствии спецификации CORRESPONDING требуется, чтобы заголовки таблиц-операндов совпадали за исключением, возможно, порядка следования столбцов.
131 С учетом возможности неявного приведения типов.
132 В следующей лекции мы более подробно обсудим подзапросы. Пока заметим, что row_subquery – это запрос, результирующая таблица которого состоит из одной строки.
133 По крайней мере, так это следует понимать в соответствии с семантикой представлений в языке SQL. При реальной обработке запросов над представлениями такая явная «материализация» представления выполняется кране редко. Вместо этого используется ехника подстановки тела представления в тело запроса с гарантией того, что результат модифицированного запроса будет в точности таким же, что и резальтат исходного запроса над материализованным представлением. Но это уже относится к тематике оптимизации SQL-запросов, выходящей за пределы этого курса.
134 Конструкция ALTER VIEW в языке SQL не поддерживается.
Назад Содержание Вперёд