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 не поддерживается.
Назад Содержание Вперёд