«Boston • San Francisco • New York London • Toronto • Sydney • Tokyo • Singapore • Madrid Mexico City • Munich • Paris • Cape Town • Hong Kong • Montreal Введение в системы баз данных К. Дж. Дейт Москва • Санкт-Петербург ...»
4.3. КАТАЛОГ Стандарт SQL включает спецификации стандартного каталога, именуемого в нем информационной схемой. Знакомые нам термины каталог и схема действительно используются в языке SQL, но с особым смыслом, характерным только для языка SQL. Вообще говоря, каталог в языке SQL состоит из дескрипторов (метаданных) для отдельной базы данных3, а схема состоит из дескрипторов той части базы данных, которая принадлежит отдельному пользователю. Другими словами, в системе может быть любое число каталогов (по одному для каждой базы данных), каждый из которых делится на произвольное число схем. Однако каждый каталог должен содержать одну и только одну схему с именем INFORMATION_SCHEMA (информационная схема), которая с точки зрения пользователя и является схемой (как уже указывалось), выполняющей функции обычного каталога.
Таким образом, информационная схема состоит из набора таблиц SQL, содержимое которых фактически отражает (точно заданным способом) все определения из всех остальных схем рассматриваемого каталога. Точнее говоря, информационная схема по умолчанию содержит набор представлений гипотетической "схемы: определения". Для поддержки схемы определения реализация не требуется, но она требуется, во-первых, для поддержки некоторого вида "схемы определения", и, во-вторых, для поддержки представлений такой "схемы определения", которые имеют вид, подобный представлениям информационной схемы. Необходимо отметить следующие особенности информационной схемы.
1. Основная причина того, что требования состоят из двух отдельных требований, сформулированных выше, заключается в том, что существующие продукты, ко нечно, поддерживают нечто близкое к "схеме определения". Однако от одного продукта к другому спектр различий таких схем будет очень широким (даже если эти продукты выпущены одним изготовителем). Поэтому имеет смысл требовать лишь то, чтобы в реализации поддерживались заранее регламентированные пред ставления "схемы определения".
2. На самом деле, речь может идти только об "одной из информационных схем", а не просто об "информационной схеме", поскольку, как мы убедились, такая схема имеется в каждом каталоге. Поэтому в общем случае все данные, которые доступны некоторому пользователю, не будут описаны с помощью единственной информа ционной схемы. Однако, чтобы упростить изложение материала, мы будем про должать считать, что в действительности существует только одна оема.
Чтобы быть более точным, необходимо отметить, что в стандарте языка SQL такое понятие, как "база данных", в действительности просто отсутствует! По определению каталогом описывается то, что называется набором данных и зависит от реализации. Однако нет никаких разумных аргументов, запрещающих подразумевать под этим понятием базу данных.
Нет смысла вдаваться здесь в детали содержимого информационной схемы. Просто приведем некоторые из наиболее важных представлений информационной схемы в надежде на то, что сами названия позволят читателю получить некоторое понятие о содержимом этих представлений. Следует также отметить, что представление TABLES содержит информацию обо всех именованных таблицах, как базовых таблицах, так и представлениях, в то время как представление VIEWS содержит информацию только о представлениях.
ASSERTIONS. Утверждения.
CHECK_CONSTRAINTS. Проверочные ограничения.
COLUMN_PRIVILEGES. Привилегии ДЛЯ столбцов.
COLUMNUDT_USAGE. Применение определяемого пользователем типа данных столбца.
COLUMNS. Столбцы.
CONSTRAINT_COLUMN_USAGE. Использование столбца ограничения.
CONSTRAINT_TABLE_USAGE. Использование таблицы ограничений.
KEY_COLUMN_USAGE. Использование столбца ключа.
REFERENTIAL_CONSTRAINTS. Ссылочные Ограничения.
SCHEMATA. Схемы.
TABLE_CONSTRAINTS. Ограничения для таблицы.
TABLE_PRIVILEGES. Привилегии для таблиц.
TABLES. Таблицы.
UDT_PRIVILEGES. Привилегии на применение типов данных, определяемых поль зователем.
USAGE_PRIVILEGES. Привилегии на использование.
USER_DEFINED_TYPES. Типы данных, определяемых пользователем.
VIEW_COLUMN_USAGE. Использование столбца представления.
VIEW_TABLE_USAGE. Использование таблицы представления.
VIEWS. Представления.
В [4.20] этот вопрос рассмотрен более подробно, в частности в этой работе описано, как формулировать запросы для получения данных из информационной схемы (а это не так просто, как можно было бы ожидать).
4.4. ПРЕДСТАВЛЕНИЯ Приведем пример определения представления на языке SQL.
CREATE VIEW GOOD_SUPPLIER
AS SELECT S#, STATUS,CITY FROM S WHERE
140 Часть I. Основные понятия А ниже приведен пример запроса SQL к этому представлению.SELECT S#, STATUS
FROM GOOD_SUPPLIER
WHERE CITY = 'London' Подставив определение представления вместо ссылки на имя представления, получим выражение, которое будет подобно приведенному ниже (обратите внимание на вложенный подзапрос в предложении FROM).GOOD_SUPPLIER. S#, GOOD_SUPPLIER
GOOD_SUPPLIER WHERE GOOD_SUPPLIER.CITY =
Это выражение может быть затем упрощено, например, следующим образом.SELECT S#, STATUS 'Lond' В последнем случае показан текст запроса, который фактически будет выполняться. В качестве второго примера рассмотрим следующую операциюБЕЬЕТЕ.
DELETE
FROM GOOD_SUPPLIER
WHERE CITY = 'London' ;Запрос на удаление, который будет фактически выполнен, выглядит следующим образом.
DELETE
WHERE STATUS > AND CITY = 'London' ;4.5. ТРАНЗАКЦИИ SQL включает непосредственные аналоги операторов BEGIN TRANSACTION, COMMIT и (см. главу 3), именуемые, соответственно, START TRANSACTION, COMMIT WORK и
ROLLBACK
ROLLBACK WORK (ключевое слово WORK не является обязательным).4.6. ВНЕДРЕНИЕ ОПЕРАТОРОВ SQL В большинстве продуктов SQL операторы языка SQL могут выполняться как непосредственно (т.е. интерактивно, с подключенного терминала), так и в виде части прикладной программы (т.е.
операторы SQL могут быть внедренными, а значит, могут смешиваться с операторами базового языка этой программы). Приложения, использующие внедренные операторы SQL, могут быть написаны на многих базовых языках; стандарт SQL включает поддержку для Ada, С, COBOL, Fortran, Java, M (прежде известного как MUMPS), Pascal и PL/I. Рассмотрим особенности технологии внедрения операторов SQL более подробно.
Фундаментальный принцип, лежащий в основе технологии внедрения операторов SQL, называется принципом двухрежимности. Он заключается в том, что любое выражение SQL, которое можно использовать интерактивно, можно применять и путем внедрения в прикладную программу. Конечно, существует множество различий в деталях между интерактивными операторами SQL и их внедренными аналогами. В частности, операции выборки требуют существенной дополнительной обработки в вычислительной среде базового языка (подробности приведены ниже в этом же разделе). Тем не менее, сам принцип двухрежимности всегда соблюдается. (Обратное, между прочим, не верно, т.е. существует несколько внедряемых операторов SQL, которые не могут использоваться интерактивно, о чем речь пойдет дальше в этой главе.) Прежде чем начать обсуждение конкретных внедряемых операторов SQL, необходимо обсудить некоторые детали. Большинство из них иллюстрируется фрагментом программы, представленным на рис. 4.3. (Для закрепления наших представлений будем считать, что базовым языком является PL/I. Большинство приводимых примеров транслируется на другие базовые языки лишь с незначительными изменениями.) Рассмотрим этот фрагмент программы.
1. Внедренные операторы SQL предваряются инструкцией EXEC SQL, так что их лег ко отличить от других операторов базового языка, и заканчиваются специальным завершающим символом (для языка PL/I таковым является точка с запятой "; ").
2. Выполняемый оператор SQL (далее и до конца этого раздела уточняющее слово вне дренный обычно не будет применяться) может быть в программе везде, где могут находиться выполняемые операторы базового языка. Обратите внимание на уточ няющее слово выполняемый: в отличие от интерактивного режима использования языка SQL, режим внедрения операторов SQL подразумевает включение в про грамму отдельных операторов SQL, которые являются чисто декларативными, а не выполняемыми. Например, оператор DECLARE CURSOR — это не выполняемый оператор (подробности приводятся в разделе "Операции, в которых используются курсоры"); таковыми не являются и операторы BEGIN и END DECLARE SECTION (см. п. 5 этого списка), а также оператор WHENEVER (СМ. П. 9).
EXEC SQL BEGIN DECLARE SECTION ;
DCL SQLSTATE CHAR(5) ; DCL P# CHAR(6) ; DCL WEIGHT FIXED DECIMAL(5,1) ;
EXEC SQL END DECLARE SECTION ;
P# = ' P2 ' ; /* Рассматривается в качестве примера */ EXEC SQL SELECT P.WEIGHT WHERE P.P# = P# ( :P# ) ; IF SQLSTATE = '00000' THEN...; /* WEIGHT - значение, полученное путем выборки */ ELSE... ; /* Возникло некоторое исключение */ Рис. 4.3. Фрагмент программы на языке PL/I с внедренными операторами языка SQL 142 Часть I. Основные понятия 3. Операторы SQL могут включать ссылки на базовые переменные (т.е. переменные базового языка). Подобные ссылки должны иметь префикс в виде двоеточия, позволяющий отличить их от имен столбцов таблиц SQL. Базовые переменные могут применяться во внедренных операторах SQL везде, где в интерактивном языке SQL могут использоваться литералы. Они могут также находиться в предложении INTO операторов SELECT (см. п. 4) и FETCH (подробности — в разделе "Операции, в которых используются курсоры"), определяющем результирующие переменные для размещения результатов выборки данных.
4. Обратите внимание на конструкцию INTO оператора SELECT, представленного на рис. 4.3. Назначение этой конструкции (как только что отмечалось) — указать ре зультирующие (целевые) переменные, в которых будут возвращены выбранные зна чения. Каждая /-я целевая переменная, указанная в предложении INTO, соответству ет /-му извлекаемому значению, указанному в списке выборки предложения SELECT.
5. Все базовые переменные, на которые ссылаются внедренные операторы SQL, долж ны быть определены в разделе объявлений внедренного языка SQL, который ограни чивается операторами BEGIN DECLARE SECTION И END DECLARE
SECTION
(в PL/I для этого используется операторDCL).6. Каждая программа, содержащая внедренные операторы SQL, должна включать ба зовую переменную с именем SQLSTATE. После выполнения любого присутствую щего в программе оператора SQL в эту переменную возвращается код состояния.
В частности, код состояния 00000 означает, что оператор был выполнен успешно, а код состояния 02000 — что оператор был выполнен, но никаких удовлетворяю щих запросу данных найдено не было (другие значения указаны в [4.23]). Таким образом, выполнение в программе каждого оператора SQL должно завершаться проверкой значения переменной SQLSTATE и, если это значение будет отличаться от ожидаемого, должны предприниматься соответствующие действия. На практи ке, однако, такая проверка обычно выполняется неявно (см. п. 9).
7. Каждая переменная базового языка должна иметь тип данных, соответствующий значениям, для хранения которых эта переменная используется. В частности, базовая переменная, используемая в качестве целевой (например, для хранения результатов операции SELECT), должна иметь тип данных, совместимый с типом выражения, значение которого присваивается этой целевой базовой переменной.
Аналогично, если базовая переменная служит источником (например, для опера ции INSERT), она должна иметь тип данных, совместимый с типом SQL того столбца, которому присваивается значение из этого источника. Но эта тема явля ется гораздо более сложной по сравнению со сведениями, приведенными в данной главе, поэтому она здесь не рассматривается (по крайней мере, достаточно под робно), а ее дальнейшее обсуждение будет продолжено в главе 5, раздел 5.7.
8. Базовые переменные для столбцов таблиц SQL могут иметь те же имена, что и имена соответствующих столбцов.
9. Как уже упоминалось, выполнение каждого оператора SQL, в принципе, должно сопровождаться проверкой значения, возвращаемого в переменной SQLSTATE.
Для упрощения этого процесса предназначен оператор WHENEVER, который имеет следующий синтаксис.
Здесь параметр (условие) может принимать значение NOT FOUND (данные не найдены), SQLWARNING (предупреждающее сообщение SQL) и SQLEXCEPTION (исключительная ситуация SQL) (другие условия включают определенные значения SQLSTATE и фиксируют нарушения заданных ограничений целостности), а параметр — это либо оператор CONTINUE (продолжить), либо оператор GO TO (перейти к метке). Оператор WHENEVER не является выполняемым; это просто директива для компилятора SQL. Наличие в программе выражения "WHENEVER GO TO " приведет к тому, что компилятор поместит оператор "IF GO TO END IF" после каждого встретившегося ему выполняемого оператора SQL. Однако, встретив выражение "WHENEVER CONTINUE", компилятор SQL не вставляет в программу никаких операторов, и следовательно, программист должен будет вставить требуемые операторы вручную. Условия NOT FOUND, SQLWARNING и SQLEXCEPTION определены, как описано ниже.
NOT FOUND показывает, что не найдены данные, соответствующие оператору;
значение SQLSTATE = 02ххх;
SQLWARNING означает, что возникла незначительная ошибка; значение SQLEXCEPTION означает, что возникла серьезная ошибка (исключительная си туация); значения SQLSTATE приведены в [4.23].
Каждый оператор WHENEVER (для определенного условия), встреченный компилятором при последовательном просмотре текста программы, отменяет предыдущий (для этого условия).
10. Используя терминологию главы 2, отметим, что внедрение операторов SQL устанавливает слабую связь между средой SQL и базовым языком.
Итак, для предварительного обсуждения этого достаточно. В остальной части этого раздела главным образом рассматриваются операторы манипулирования данными. Как уже отмечалось, большинство из них можно использовать практически в неизменном виде (т.е. лишь с незначительными изменениями в синтаксисе). Однако операции выборки требуют отдельного описания. Проблема состоит в том, что такие операторы в общем случае выбирают не одну, а множество строк, в то время как процедурные базовые языки обычно не приспособлены для выборки больше одной строки за одно обращение.
Следовательно, необходимо создать своего рода "мост" между предусмотренными в языке SQL средствами выборки, позволяющими получать одновременно множество строк, и применяемыми в базовом языке средствами обработки, допускающими одновременное использование только одной строки. В качестве подобного "моста" используются курсоры. Курсор представляет собой своего рода логический указатель, который может использоваться в приложении для перемещения по набору строк, указывая поочередно на каждую из них и таким образом обеспечивая возможность адресации этих строк — по одной за один раз. Однако временно отложим подробное обсуждение курсоров (до подраздела "Операции, в которых используются курсоры") и рассмотрим сначала такие операторы, для которых курсоры не требуются.
144 Часть I. Основные понятия Операции, в которых не используются курсоры Ниже перечислены операторы манипулирования данными, для которых не требуется использование курсоров.
Однострочный оператор SELECT.
INSERT.
UPDATE (кроме формы CURRENT — см. следующий подраздел).
DELETE (также кроме формы CURRENT — см. следующий подраздел).
Рассмотрим примеры каждого из этих операторов.
Однострочный оператор SELECT. Получить статус и название города для поставщика, номер которого задан в базовой переменной GlVENS#.
EXEC SQL SELECT STATUS, CITY
INTO :RANK, :TOWN FROM S Термин однострочный оператор SELECT используется для обозначения выражения SELECT, значением которого будет таблица, содержащая не больше одной строки.В данном примере, если в таблице S существует одна и только одна строка, отвечающая заданному условию в конструкции WHERE, значения столбцов STATUS и CITY из этой строки в соответствии с запросом будут присвоены базовым переменным RANK и CITY, а переменной SQLSTATE будет присвоено значение 00000.
Если в таблице S нет ни одной строки, отвечающей заданному условию WHERE, переменной SQLSTATE будет присвоено значение 02000. Если же таких строк окажется больше одной, будет зафиксирована ошибка и переменная SQLSTATE будет содержать ее код.
Оператор INSERT. Вставить в таблицу Р сведения о новой детали (номер детали, ее название и вес задаются содержимым базовых переменных Р#, PNAME, PWT, соответственно; цвет детали и город неизвестны).
EXEC SQL INSERT
INTO P ( P#, PNAME, WEIGHT ) VALUES ( :P#, :PNAME, :PWT ) Столбцам COLOR и CITY вновь добавляемой строки таблицы будут присвоены соответствующие значения, применяемые по умолчанию. Подробнее об этом речь пойдет в разделе 6.6 главы 6. Следует отметить, что по причинам, анализ которых выходит за рамки данной книги, применяемое по умолчанию значение для столбца, который имеет некоторый тип, определяемый пользователем, обязательно должно быть неопределенным (NULL). (Подробное обсуждение темы, касающейся неопределенных значений, откладывается до главы 19; однако нам неизбежно придется время от времени возвращаться к этому вопросу.) Оператор DELETE. Удалить сведения обо всех поставках для поставщиков из города, название которого помещено в базовую переменную CITY.
EXEC SQL DELETE
FROM SP
( SELECT CITY FROM S
И снова, если нет строк, удовлетворяющих условию WHERE, переменной SQLSTATE присваивается значение 02000. Также обратите внимание на вложенный подзапрос (на этот раз в предложении WHERE).Оператор UPDATE. Увеличить статус всех поставщиков из Лондона на значение, помещенное в базовую переменную RAISE.
EXEC SQL UPDATE S
Если в таблице поставщиков не будет найдено строк, удовлетворяющих условию WHERE, система присвоит переменной SQLSTATE значение 02000.Операции, в которых используются курсоры Теперь перейдем к вопросу о выборках на уровне множеств, т.е. о выборках не одной строки, как это было в случае однострочного оператора SELECT, а множества с произвольным количеством строк. Как указывалось ранее, в этой ситуации потребуется поочередный доступ к строкам выбранного множества, а механизмом такого доступа будет курсор. На рис. 4.4 этот процесс схематически проиллюстрирован на примере выборки информации о поставщиках (столбцы s#, SNAME и STATUS) для всех поставщиков из города, название которого задается в базовой переменной У.
EXEC SQL DECLARE X CURSOR FOR /* Определить курсор */ Рис. 4.4. Выборка нескольких строк Пояснение. Оператор DECLARE X CURSOR... определяет курсор х, связанный с табличным выражением (т.е. выражением, которое возвращает таблицу). Табличное выражение определяется оператором SELECT, который является частью всего выражения 146 Часть I. Основные понятия DECLARE, но не вычисляется в этом месте программы, поскольку оператор DECLARE CURSOR— чисто декларативный. Табличное выражение вычисляется только при открытии курсора (оператор OPEN X). Далее для выборки строк из результирующего множества, по одной за один раз, используется оператор FETCH X INTO..., присваивающий извлеченные значения базовым переменным в соответствии со спецификациями в конструкции INTO. (Для простоты базовым переменным присвоены имена, совпадающие с именами соответствующих столбцов таблицы базы данных. Обратите внимание, что в операторе SELECT при определении курсора не задается собственная конструкция INTO.) Поскольку в результирующем наборе потенциально присутствует большое количество строк, оператор FETCH обычно вызывается в цикле. Цикл будет повторяться до тех пор, пока не закончатся строки в результирующем наборе. После выхода из цикла курсор х закрывается (оператор CLOSE X).
А теперь рассмотрим курсоры и операции с ними более подробно. Курсор определяется с помощью оператора DECLARE CURSOR, который имеет следующий общий вид.
EXEC SQL DECLARE CURSOR
Для краткости несколько необязательных спецификаций в этом определении не указаны. Здесь параметр — это имя определяемого курсора. Необязательный параметр определения сортировки результата выборки имеет следующий формат.
ORDER BY
Здесь параметр — разделенный запятыми список элементов, по которым должно быть выполнено упорядочение извлекаемых строк. Список должен содержать не меньше одного элемента < order i tem>, а в каждом элементе списка должно содержаться имя столбца (заметьте, неуточненное)4, после которого может следовать необязательное служебное слово ASC (по возрастанию) или DESC (по убыванию). При отсутствии служебного слова по умолчанию принимается порядок по возрастанию (ASC). Если конструкция ORDER BY не определена, то принцип упорядочения определяется системой. (Фактически, последнее замечание остается в силе, если даже определена конструкция ORDER BY, по крайней мере, когда речь идет о строках с одним и тем же значением для указанного списка< order item commalist>.) Примечание. Дадим определение удобному термину разделенный запятыми список элементов (commalist). Пусть обозначает произвольную синтаксическую категорию (т.е. то, что находится слева от некоторого правила вывода в нотации BNF). Тогда выражение (или ) обозначает последовательность от нуля и больше элементов, в которой каждая пара элементов разделена Фактически, имя столбца может быть уточнено, если заданное табличное выражение соответствует довольно сложному набору правил. Эти правила были впервые введены в стандарте SQL: 1999, в котором также регламентированы правила, согласно которым элемент может иногда определять либо вычислительное выражение, например, ORDER BY A+B, либо имя столбца, который входит в состав таблицы результата, например, SELECT CITY FROM S ORDER BY STATUS.Изложение подробных сведений об этих правилах выходит за рамки настоящей книги.
запятой (и возможно также одним или несколькими пробелами). Обозначение в виде списка элементов, разделенного запятыми, будет широко использоваться в приводимых далее синтаксических правилах (причем во всех синтаксических правилах, а не только в правилах языка SQL).
Как утверждалось ранее, оператор DECLARE CURSOR — декларативный, а не выполняемый. Он предназначен для объявления курсора с определенным именем, для которого предусмотрено табличное выражение и с которым постоянно связан тип упорядочения. Табличное выражение может включать ссылки на базовые переменные. Программа может содержать любое количество операторов DECLARE CURSOR, каждый из которых должен быть, конечно, предназначен для определения разных курсоров.
Для работы с курсорами существует три выполняемых оператора: OPEN, FETCH И CLOSE.
Оператор OPEN имеет следующий формат.
EXEC SQL OPEN ;
Он предназначен для открытия или активизации указанного курсора (который в данный момент не должен быть открыт). В результате его выполнения вычисляется связанное с этим курсором табличное выражение (причем для всех базовых переменных, упоминаемых в этом выражении, используются текущие значения). В результате идентифицируется определенное множество строк, которое становится текущим активным набором для данного курсора. Курсор также устанавливает исходную позицию в этом активном наборе, а именно — позицию перед его первой строкой. Следует отметить, что активные наборы всегда рассматриваются как упорядоченные (см. приведенное выше описание конструкции ORDER BY), а значит, и понятие позиции имеет для них смысл5.Оператор FETCH имеет следующий формат.
EXEC SQL FETCH
Здесь параметр —разделенный запятыми список ссылок на базовые переменные. Этот оператор служит для перемещения позиции указанного курсора (который должен быть уже открыт) к следующей строке в его активном наборе с последующим присваиванием значений столбцов этой строки базовым переменным, указанным в предложении INTO.Если после очередного вызова оператора FETCH на выполнение следующая строка отсутствует, то выборка данных не производится и переменной SQLSTATE присваивается значение 02 000.
Оператор CLOSE имеет следующий формат.
EXEC SQL CLOSE ;
Сами по себе множества, конечно, не являются упорядоченными (глава 6), так что "активный набор" — это на самом деле не множество как таковое. Его лучше представлять в виде упорядоченного списка или массива (строк).148 Часть I. Основные понятия Он служит для закрытия (деактивизации) указанного курсора (который должен быть в данный момент открытым). После его выполнения с курсором уже не будет связан активный набор. Однако в дальнейшем курсор вновь может быть открыт;
при этом он снова получит активный набор — возможно, уже не такой, как раньше (в частности, если значения указанных в объявлении курсора базовых переменных к текущему моменту были изменены). Следует отметить, что изменение этих переменных при открытом курсоре не оказывает влияния на его активный набор.
Есть еще два оператора, в которых могут использоваться ссылки на курсоры, — это варианты операторов UPDATE и DELETE С конструкцией CURRENT. ЕСЛИ курсор (скажем, х) в данный момент позиционирован на определенную строку, то можно обновить или удалить эту "текущую строку курсора х", т.е. строку, на которую в данный момент позиционирован курсор х, например, как показано ниже.
EXEC SQL UPDATE S
:RAISE WHERE CURRENT OF X
Выражения CURRENT операторов DELETE и UPDATE будут недопустимыми, если табличное выражение < table exp> в объявлении курсора определено с участием необновляемого представления, созданного с помощью оператора CREATE VIEW (подробности приведены в главе 10, раздел 10.6).Динамический язык SQL и интерфейс SQL/CLI В предыдущем разделе по умолчанию предполагалось, что данная конкретная программа (включая операторы SQL и все операторы базового языка) может быть полностью откомпилирована "заблаговременно" (т.е. до наступления этапа прогона) в том виде, в каком она реализуется на данный момент. Однако применительно к некоторым приложениям соблюдение такого условия нельзя гарантировать. Например, рассмотрим оперативное приложение. (Напомним, что, как отмечалось в главе 1, оперативными называются приложения, которые предоставляют пользователю доступ к базе данных с некоторого интерактивного терминала или подобного ему устройства.) Как правило, в этом приложении должны выполняться примерно такие действия, как показано ниже.
1. Принять с терминала команду пользователя.
2. Проанализировать поступившую команду.
3. Сгенерировать соответствующие операторы SQL для обращения к базе данных.
4. Возвратить сообщение и (или) полученные результаты на терминал.
Если набор команд пользователя, который программа может принять с терминала в шаге 1, достаточно мал (как, например, в случае обработки предварительных заказов мест на авиалиниях), то набор всех возможных выполняемых операторов SQL также будет невелик и его можно будет непосредственно внедрить в программу. В этом случае действия на втором и третьем этапах будут состоять в логической проверке введенной команды с последующим переходом к той части программы, которая выполняет заранее предопределенные операторы SQL. С другой стороны, если набор вводимых команд достаточно разнообразен, было бы непрактично заранее предопределять и внедрять в программу все необходимые выражения SQL, соответствующие всем возможным командам. Вместо этого, вероятно, целесообразнее конструировать необходимые запросы SQL динамически, а затем динамически же компилировать и выполнять сконструированные запросы.
Средства динамического языка SQL, описанные в этом разделе, предназначены для поддержки данного процесса.
Динамический язык SQL Средства динамического языка SQL — это часть средств поддержки внедрения языка SQL. Они состоят из множества динамических операторов (компиляция самих этих операторов осуществляется заблаговременно), назначение которых состоит именно в том, чтобы обеспечивать поддержку трансляции и выполнения обычных операторов SQL, создаваемых на этапе прогона программы. Таким образом, существуют два основных динамических оператора SQL — PREPARE (Подготовить — по сути, компилировать) и EXECUTE (Выполнить). Их использование проиллюстрировано на следующем (нереальном по своей простоте, но достаточно точном) примере на языке PL/I.
DCL SQLSOURCE CHAR VARYING (65000) ;
SQLSOURCE = 'DELETE FROM SP WHERE QTY < QTY (
)' ; EXEC SQL PREPARE SQLPREPPED FROM :SQLSOURCE ;
EXEC SQL EXECUTE SQLPREPPED ;
Пояснения 1. Имя SQLSOURCE идентифицирует переменную языка PL/I типа символьной стро ки переменной длины, в которой на этапе прогона программа определенным об разом подготавливает исходную форму (т.е. представление в виде символьной строки) некоторого оператора SQL, в нашем конкретном примере — оператора 2. Имя SQLPREPPED, напротив, идентифицирует переменную среды SQL, а не базового языка PL/I, которая будет (в принципе) использоваться для хранения компилиро ванной формы оператора SQL (исходная форма которого представлена в перемен ной SQLSOURCE). Конечно, имена, подобные SQLSOURCE И SQLPREPPED, МОЖНО выбирать произвольно.3. С помощью оператора присваивания SQLSOURCE =...; на языке PL/I перемен ной SQLSOURCE присваивается исходная форма оператора SQL DELETE. Конечно, на практике процесс формирования такого исходного оператора будет значитель но сложнее и, возможно, в нем будут использоваться ввод и анализ некоторых элементов запросов от конечного пользователя, выраженных на естественном языке или в другой, более "дружественной" форме, чем обыкновенный язык SQL.
4. Оператор PREPARE извлекает это исходное выражение и подготавливает (т.е. ком пилирует) его, создавая выполняемую версию извлеченного им оператора, сохра няемую в переменной SQLPREPPED.
5. Оператор EXECUTE выполняет откомпилированную версию оператора из пере менной SQLPREPPED, в результате чего осуществляется собственно операция DELETE. Информация SQLSTATE выполненного оператора DELETE возвращается так же, как при выполнении аналогичного оператора удаления, обычным сбразом.
150 Часть I. Основные понятия Обратите внимание, что имя SQLPREPPED идентифицирует переменную языка SQL, a не PL/I, поэтому при его использовании в операторах PREPARE И EXECUTE двоеточие перед ним не указывается. Важно также, что подобные переменные SQL явно не объявляются.
Следует отметить, что описанный выше процесс в точности совпадает с процессом, который происходит, если выражения SQL вводятся интерактивно. Во многих системах имеется некоторое подобие процессора запросов SQL. Этот процессор в действительности — не что иное, как обобщенное интерактивное приложение, способное обрабатывать весьма широкий спектр вводимых команд, а именно — любой допустимый (или недопустимый!) оператор языка SQL. Для конструирования операторов SQL, соответствующих вводимым пользователем командам, для компиляции и выполнения сконструированных операторов и для возврата сообщений и результатов на терминал в нем используются именно средства динамического языка SQL.
Безусловно, средства динамической поддержки SQL не исчерпываются описанными выше операторами PREPARE И EXECUTE; например, в них предусмотрены механизмы параметризации подготавливаемых операторов и подстановки фактических параметров вместо формальных параметров перед выполнением динамически сформированных операторов; кроме того, имеются аналоги тех средств курсоров, которые описаны в предыдущем разделе. В частности, в состав этих средств входит оператор EXECUTE IMMEDIATE, который фактически позволяет объединить функции PREPARE И EXECUTE В ОДНОЙ операции.
Интерфейсы уровня вызовов SQL/CLI Средства интерфейса уровня вызовов SQL (SQL Call-Level Interface — SQL/CLI, или сокращенно CLI) были введены в стандарт SQL в 1995 году. Интерфейс CLI в основном создан на базе интерфейса ODBC (Open Database Connectivity) компании Microsoft. И тот, и другой интерфейс позволяет приложениям, которые написаны на одном из базовых языков, выдавать запросы к базе данных, обращаясь к процедурам СLI, предоставляемым изготовителем, не прибегая к помощи внедренных операторов SQL. Затем в этих процедурах, которые предварительно должны быть обязательно связаны с данным приложением, используется динамический язык SQL для выполнения требуемых операций с базой данных от имени приложения. (Иными словами, с точки зрения СУБД процедуры CLI ничем не отличаются от обычного приложения.) Как видим, интерфейс SQL/CLI (а также ODBC) решает ту же задачу, что и динамический язык SQL, а именно — позволяет приложению передавать на выполнение текст оператора SQL именно к тому времени, когда его непосредственно необходимо выполнять. Однако применяемый в интерфейсах CLI и ODBC подход к решению этой задачи организован лучше, чем в динамическом языке SQL. Его преимущества заключаются в следующем.
Во-первых, динамический язык SQL — это исходный код, который должен соответствовать стандарту SQL. Поэтому для любого приложения, которое использует динамический язык SQL, требуется какой-то компилятор SQL, необходимый для обработки установленных стандартом операций, таких как PREPARE, EXECUTE и т.д. Интерфейсом CLI, напротив, нормированы лишь подробности вызова процедур (т.е. в основном вызова подпрограмм). Не требуются средства специального компилятора; достаточно использовать обычный компилятор стандартного базового языка. Поэтому приложение может распространяться (возможно даже сторонними изготовителями программного обеспечения) в "сжатой" форме, в виде объектного кода.
Во-вторых, такие приложения могут быть независимыми от типа СУБД, т.е. интерфейс SQL/CLI включает средства создания универсальных приложений (опять же, возможно, сторонними изготовителями программного обеспечения), которые могут использоваться для нескольких различных типов СУБД, без специализации с учетом какой-то конкретной СУБД.
Ниже в качестве иллюстрации использования интерфейса SQL/CLI приведен аналог примера динамического вызова SQL, который можно найти в предыдущем подразделе.
char sqlsource [65000] ;
strcpy ( sqlsource, rc = SQLExecDirect ( hstmt, (SQLCHAR *)sqlsource, SQL_NTS ) ;
Пояснение 1. Поскольку в реальных приложениях SQL/CLI базовым языком обычно служит С, в данном примере вместо PL/I используется С. Кроме того, в соответствии со спе цификацией SQL/CLI в именах переменных, именах процедур и т.п. используются прописные буквы (или сочетание прописных и строчных букв), а не только про писные буквы, как во всех остальных именах в этой книге (кроме того, в этих по яснительных примечаниях имена выделены для наглядности моноширинным шрифтом). Необходимо также учитывать, что интерфейс SQL/CLI представляет собой набор стандартных средств для вызова подпрограмм из базового языка, по этому его синтаксис (а также, безусловно, соответствующая семантика) изменяет ся в зависимости от базового языка.
2. Функция strcpy языка С вызывается для копирования исходной формы опреде ленного оператора DELETE языка SQL в переменную sqlsource языка С.
3. Оператор присваивания С ("=") вызывает процедуру SQLExecDirect интерфейса SQL/CLI (аналог оператора EXECUTE IMMEDIATE динамического языка SQL) для выполнения оператора SQL, содержащегося в переменной sqlsource, и при сваивает переменной гс программы С код возврата, полученный в результате этого вызова.
Как и можно было бы предположить, интерфейс SQL/CLI в той или иной степени включает аналоги всех средств динамического выполнения SQL, наряду с некоторыми дополнениями. Более подробное описание этого интерфейса выходит за рамки данной книги. Однако следует учитывать, что такие интерфейсы, как CLI, ODBC и JDBC (фактически вариант ODBC для языка Java), приобретают все более важное практическое значение по причинам, которые будут обсуждаться в главе21, раздел 21.6.
152 Часть I. Основные понятия 4.7. НЕСОВЕРШЕНСТВО ЯЗЫКА SQL Как отмечалось в разделе 4.1 этой главы, язык SQL отнюдь нельзя назвать "совершенным" реляционным языком, поскольку он имеет много недостатков, вызванных многочисленными недоделками и переделками. Конкретные критические замечания будут представлены в следующих главах. Отметим лишь основной недостаток, который заключается в том, что в целом язык SQL, строго говоря, некорректно поддерживает реляционную модель. Поэтому возникает сомнение, действительно ли современные продукты SQL заслужили право называться реляционными. Фактически, насколько это известно автору, на сегодняшний день на рынке нет ни одного продукта, который поддерживал бы реляционную модель в полном объеме6. Мы не хотим этим сказать, что если современные продукты обходятся без каких-то элементов реляционной модели, то последние не очень важны; напротив, в модели важен каждый элемент. Более того, каждый из ее элементов важен исключительно по практическим соображениям. Нельзя не подчеркнуть тот непреложный факт, что назначение реляционной теории состоит не в том, чтобы быть просто "теорией ради теории". Вовсе нет, ее назначение — заложить основу для построения систем, которые будут практически применимыми на все сто процентов. Но, как это ни печально, со стороны изготовителей продуктов еще не сделано реальных шагов к решению проблемы реализации реляционной теории во всей ее полноте. В результате, с позволения сказать, "реляционные" продукты сегодняшнего дня все как один по тем или иным причинам оказываются неспособными реализовать преимущества, которые могут быть достигнуты в результате использования реляционной технологии в потном объеме.
4.8. РЕЗЮМЕ На этом завершается знакомство с некоторыми важнейшими функциональными возможностями, предусмотренными стандартом языка SQL. Мы подчеркиваем тот факт, что язык SQL очень важен с коммерческой точки зрения, хотя, к сожалению, в определенной степени несовершенен с чисто реляционной точки зрения.
Язык SQL состоит из двух компонентов: языка определения данных (Data Definition Language — DDL) и языка манипулирования данными (Data Manipulation Language — DML). Язык манипулирования данными может применяться и на внешнем уровне (по отношению к представлениям), и на концептуальном уровне (по отношению к базовым таблицам). Аналогично, язык определения данных может использоваться для определения объектов на внешнем уровне (представления), концептуальном уровне (базовые таблицы), а в большинстве коммерческих систем (хотя это и не соответствует стандарту как таковому) даже на внутреннем уровне (по отношению к индексам и другим физическим структурам организации памяти). Кроме того, язык SQL предоставляет определенные возможности управления данными, т.е. возможности, которые нельзя отнести ни к языку DDL, ни к языку DML. Примером здесь может служить оператор GRANT, который одни пользователи могут применять для предоставления привилегий доступа другим пользователям системы (глава 17).
Было показано, как можно использовать язык SQL для создания базовых таблиц с помощью оператора CREATE TABLE (кроме того, был кратко рассмотрен оператор CREATE TYPE). Затем было приведено несколько примеров использования операторов Однако см. [20.1].
SELECT, INSERT, DELETE
применять оператор SELECT для реализации реляционных операций сокращения, проекции и соединения. Также некоторое внимание было уделено информационной схеме, состоящей из множества предопределенных представлений гипотетической "схемы определения", и возможностям языка SQL по работе с представлениями и транзакциями.Значительная часть этой главы была посвящена внедренным операторам SQL.
Основной замысел, лежащий в основе использования внедренных операторов SQL, называется принципом двухрежимности, т.е. принципом, в соответствии с которым (насколько это возможно) любое выражение SQL, которое можно использовать интерактивно, можно внедрить и в прикладную программу. Главное исключение из этого принципа имеет место в связи с операциями многострочной выборки, для которых требуется использовать курсоры, позволяющие преодолеть разрыв между возможностью выборки данных на уровне множеств в языке SQL и возможностью выборки данных на уровне строки в базовых языках программирования, таких, например, как PL/I.
Далее мы обсуждали главным образом вопросы синтаксиса, в том числе выяснили назначение переменной SQLSTATE И рассмотрели такие операторы, как однострочный оператор SELECT и операторы INSERT, DELETE И UPDATE, ДЛЯ которых курсор не нужен.
Затем мы обратились к операторам, для которых действительно требуется использование курсора, и обсудили операторы DECLARE CURSOR, OPEN, FETCH, CLOSE, а также операторы DELETE и UPDATE с конструкцией CURRENT. (В стандарте языка SQL форму этих операторов с конструкцией CURRENT называют, соответственно, позиционным оператором UPDATE и DELETE, а термин поисковый используют для других форм этих операторов, отличных от использующих CURRENT.) Наконец, мы кратко обсудили концепцию динамического языка SQL, в частности — операторы PREPARE и EXECUTE, а также кратко коснулись (в сравнении с ODBC и JDBC) назначения интерфейса уровня вызовов SQL (SQL CallLevel Interface — SQL/CLI).
УПРАЖНЕНИЯ
4.1. На рис. 4.5 показаны примеры значений данных для расширенной формы базы данных поставщиков и деталей, которая называется базой данных поставщиков, деталей и проектов1. Поставщики (S), детали (р) и проекты (J) однозначно определяются, соответственно, номером поставщика (s#), номером детали (р#) и номером проекта (J#). Значение предиката для отношения SPJ (поставки) таково:определенный поставщик S# поставляет определенную деталь Р# для определенного проекта J# в определенном количестве QTY (причем комбинация значений столбцов {S#, Р#, J#} представляет собой первичный ключ). Запишите соответствующие определения данных на языке SQL для этой базы данных.
Примечание. Эта база данных будет использоваться во многих упражнениях в госледующих главах.
4.2. В разделе 4.2 был описан оператор CREATE TABLE так, как он определен в стандарте языка SQL. Однако многие коммерческие продукты поддерживают дополнительные опции этого оператора, обычно связанные с индексами, размещением Рис. 4.5 приведен на стр. 154 (а рис. 3.8 — на стр. 119).
154 Часть I. Основные понятия на дисковом пространстве и другими вопросами реализации, что противоречит требованиям обеспечения физической независимости от данных и междусистемной совместимости. Исследуйте доступный вам продукт, поддерживающий язык SQL. Относятся ли предыдущие замечания к этому продукту? В частности, какие дополнительные опции оператора CREATE TABLE поддерживаются в этом проИ снова исследуйте доступный вам продукт, поддерживающий язык SQL. Под держивается ли в нем информационная схема? Если нет, то каким образом под держивается каталог?
4.4. Сформулируйте на языке SQL следующие операции обновления для базы данных поставщиков, деталей и проектов.
а) Вставить данные нового поставщика S10 в таблицу S; имя поставщика — Smith, город — New York, статус еще не известен.
б) Удалить все проекты, для которых нет поставок.
в) Изменить цвет всех деталей красного цвета (red) на оранжевый (orange).
Рис. 4.5. База данных поставщиков, деталей и проектов (значения приведены в качестве примера) 4.1. Используя базу данных поставщиков, деталей и проектов, напишите программу с внедренными выражениями SQL для выдачи списка всех строк поставщиков по порядку их номеров. За каждой строкой поставщика должны непосредственно следовать строки проектов, обеспечиваемых этим поставщиком, по порядку номеров проектов.
4.2. Даны таблицы PART И PART_STRUCTURE, определенные следующим образом.
CREATE TABLE PART
; CREATE TABLE
PART_STRUCTURE
FOREIGN KEY ( MAJOR_P# ) REFERENCES
REFERENCES PART ) ;
В таблице PART_STRUCTURE показано, какие детали (MAJOR_P#) содержат другие детали (MINOR_P#) как компоненты первого уровня. Напишите на языке SQL программу для получения списка всех компонентов данной детали на всех имеющихся уровнях (задача разузлования деталей). Примечание. Значения, показанные в качестве примера на рис. 4.6, могут помочь вам более наглядно представить предложенную выше задачу. Следует отметить, что таблица PART_STRUCTURE демонстрирует, как информация о составе изделий (см. главу 1, раздел 1.3, подраздел "Сущности и связи") обычно представляется в реляционных системах.Рис. 4.6. Таблица PART_STRUCTURE (значения приведены в качестве примера)
СПИСОК ЛИТЕРАТУРЫ
В [3.3] (приложение Н) приведены результаты подробного сравнения спецификации SQL: 1999 и предложений Третьего Манифеста. См. также приложение Б настоящей книги.4.1. Astrahan M.M., Lorie R.A. SEQUEL-XRM: A Relational System // Proc. ACM Pacific Regional Conference. — San Francisco, Calif., April 1975.
Описан первый прототип реализации языка SEQUEL— самой ранней версии языка SQL [4.9]. См. также документы [4.2] и [4.3], которые выполняют аналогичную функцию для проекта System R.
4.2. Astrahan M.M. et al. System R: Relational Approach to Database Management // ACM 156 Часть I. Основные понятия Система System R была реализацией основного прототипа (ранней версии — языка SEQUEL/2, см. [4.10]) языка SQL. В статье описывается архитектура System R в том виде, в каком она была изначально запланирована; см. также [4.3].
4.3. Blasgen M.W. et al. System R: An Architectural Overview // IBM Sys. J. — February Описывается архитектура System R на тот момент, когда система была полностью реализована (ср. с [4.2]).
4.4. Celko J. SQL for Smarties: Advanced SQL Programming. San Francisco, Calif.: Morgan Kaufmann, 1995.
"Это первая вышедшая из печати книга по языку SQL с глубоким и детальным освещением материала, в которой исчерпывающе представлены средства и методы, позволяющие совершенствовать свои навыки читателю, от неопытного пользователя языка SQL до высококвалифицированного программиста" (цитата с обложки книги).
4.5. Chaudhuri S., Weikum G. Rethinking Database System Architecture: Towards a SelfTuning RISC-Style Database System // Proc. 26th Int. Conf. on Very Large Data Bases, Cairo, Egypt. — September 2000.
Эта статья содержит некоторые серьезные критические замечания в адрес SQL.
Приведем одну цитату: "SQL — это источник неприятностей. Одной из основных трудностей, связанных с использованием систем с базами данных, является именно язык SQL. Он представляет собой объединение всех тех средств, которые только можно себе представить (многие из которых редко используются или, во всяком случае, не должны быть рекомендованы для использования) и слишком сложен для разработчика приложений среднего уровня. Ядро этого языка, к которому можно отнести запросы с операторами выборки, проекции и соединения, а также операции агрегирования, является чрезвычайно полезным, но мы сомневаемся, что все его дополнительные возможности оправданы и достойны широкого применения. Попытка понять семантику спецификации SQL: 1992, не говоря уже о SQL: 1999, изучить все комбинации вложенных (и коррелированных) подзапросов, неопределенных значений, триггеров, функций поддержки абстрактных типов данных (
Abstract
Data Type — ADT) и т.д. становится буквально пыткой. Обучение языку SQL обычно ограничивается ядром этого языка, после чего преподаватели рекомендуют слушателям в качестве домашнего задания освоить необходимые дополнительные средства языка на собственном практическом опыте. В некоторых отраслевых журналах иногда публикуются задачи по языку SQL, в которых требуется выразить сложный информационный запрос в виде одного оператора SQL.
Такие операторы занимают несколько страниц и едва ли постижимы".
4.6. Eisenberg A., Melton J. SQL: 1999, Formerly Known as SQL3 // ACM SIGMOD Record. — March 1999. — 28, № 4.
Краткое вводное описание новых средств, которые были добавлены в стандарт SQL в результате публикации спецификации SQL: 1999.
4.7. Eisenberg A. and Melton J. SQLJ Part 0, Now Known as SQL/OLB (Object Language Bindings) //ACM SIGMOD. — December 1998. — 27, № 4; Eisenberg A. and Melton J. SQLJ— Part 1: SQL Routines Using the Java™ Programming Language // ACM SIGMOD. — December 1999. — 28, № 4. См. также Gray Clossman et al. Java and Relational Databases: SQLJ // Proc. 1998 ACM SIGMOD Int. Conf. on Management of Data, Seattle, Wash. — June 1998.
Название SQLJ первоначально было присвоено проекту, в котором рассматривались возможности интеграции SQL и Java (в этом проекте совместно участвовали некоторых из самых известных поставщиков программного обеспечения SQL).
В части 0 этого проекта изучались способы внедрения операторов SQL в программы на языке Java, в части 1 реализовывалась идея вызова компонентов Java из среды SQL (например, вызова хранимых процедур, написанных на языке Java, — см. главу 21); а часть 2 была посвящена анализу возможностей использования классов Java в качестве типов данных SQL (например, как основы для определения столбцов в таблицах SQL). Часть 0 была включена в спецификацию SQL: 1999, а части 1 и 2 будут почти наверняка включены в окончательную спецификацию SQL:2003 (см. аннотацию к [4.23]).
4.8. Chamberlin D. Using the New DB2. — San Francisco, Calif.: Morgan Kaufmann, 1996.
Увлекательное и всестороннее описание современного положения дел в отношении коммерческих продуктов SQL, сделанное одним из основных разработчиков первоначальной версии языка SQL [4.9]—[4.11].
Примечание. В этой книге Чемберлена обсуждаются также "некоторые спорные решения", воплощенные в проекте языка SQL (прежде всего, касающиеся поддержки неопределенных значений и допустимости появления дубликатов строк).
Как писал сам автор этой книги, Дональд Чемберлен: "Я стремился... в основном излагать исторические сведения, а не доказывать, что принятые решения были правильными; я считаю, что каждый должен решить, следует ли поддерживать неопределенные значения и дубликаты, на основании собственных убеждений...;.
Разработчики [языка SQL] главным образом были практиками, а не теоретиками, и такая их направленность отразилась на многих решениях [проекта]". Подобная позиция очень отличается от той, которую представляет автор настоящей книги!
Вопросы поддержки неопределенных значений и дубликатов должны решаться на основании научных исследований, а не собственных убеждений; они обсуждаются в настоящей книге с научной точки зрения в главах 19 и 6, соответственно. А что касается противопоставления "практиков теоретикам", то мы категорически отвергаем мнение о непрактичности теории. Мы уже заявили о своей позиции по этому вопросу (в разделе 4.8), отметив, что теория, по крайней мере, реляционная, по своей сути очень даже практична.
4.9. Chamberlin D.D. and Boyce R.F. SEQUEL: A Structured English Query Language // Proc. ACM SIGMOD Workshop on Data Description, Access, and Control. — Ann Arbor, Mich. — May 1974.
В статье впервые представлен язык SQL (или SEQUEL, как он назывался вначале;
впоследствии название по правовым соображениям было изменено).
4.10. Chamberlin D.D. et al. SEQUEL/2: A Unified Approach to Data Definition, Manipulation, and Control // IBM J. R&D. — November 1976. — 20, № 6; см. также исправления и дополнения к этой статье. // IBM J. R&D. — January 1977. — 21, № 1.
158 Часть I. Основные понятия Опыт реализации предыдущего прототипа языка SEQUEL, описанного в [4.1], и результаты проверок практической применимости привели к разработке новой версии языка, названной SEQUEL/2. Язык, поддерживаемый системой System R [4.2], [4.3], был в основном похож на SEQUEL/2 (с заметным отсутствием возможностей так называемых утверждений и триггеров; подробности приводятся в главе 9), плюс некоторые расширения, появившиеся в результате учета опыта пользователей [4.11].
4.11. Chamberlin D.D. A Summary of User Experience with the SQL Data Sublanguage // Proc. Int. Conf. on Database. — Aberdeen, Scotland, July 1980. (См. также IBM Research Report RJ2767. — April 1980.) В статье обсуждается ранний опыт использования системы System R и предлагаются некоторые дополнения к языку SQL на основании этого опыта. Некоторые из этих дополнений — операторы EXISTS, LIKE, PREPARE и EXECUTE — действительно были реализованы в окончательной версии System R. Они описаны в разделе 8.6 (EXISTS), приложении Б (LIKE) и разделе 4.7 (PREPARE и 4.12. Chamberlin D.D. et al. Support for Repetitive Transactions and Ad Hoc Queries in System R//ACM TODS. - March 1981. - 6, № 1.
В статье приводятся некоторые результаты оценки производительности системы System R как в среде выполнения произвольных запросов, так и в среде выполнения стандартных транзакций. {Стандартная транзакция — это простое приложение, которое имеет доступ лишь к небольшой части базы данных и перед выполнением предварительно компилируется. Это соответствует тому, что мы называли планируемым запросом в разделе 2.8 главы 2.) Эта статья, кроме всего прочего, демонстрирует, что в системе, подобной System R, компиляция почти всегда превосходит интерпретацию по итоговой производительности, даже при выполнении произвольных запросов, и что система приобретает способность выполнять несколько стандартных транзакций в секунду, если в базе данных предусмотрены соответствующие индексы.
Эта статья достойна внимания, поскольку она была одной из первых статей, показавших несостоятельность заявлений (которые в то время приходилось слышать очень часто), что "реляционные системы никогда не будут иметь хорошие эксплуатационные характеристики". Безусловно, со времени этой первой публикации коммерческие реляционные продукты достигли такой производительности, что за секунду могут выполняться сотни и даже тысячи транзакций.
4.13. Chamberlin D.D. et al. A History and Evaluation of System R // CACM. — October Описываются три основные фазы развития проекта System R (предварительный прототип, многопользовательский прототип и оценочный вариант); основное внимание уделяется технологиям компиляции и оптимизации, которые впервые использовались в System R. Частично в этой статье обсуждаются те же вопросы, 4.14. Chamberlin D.D., Gilbert A.M., Yost R.A. A History of System R and SQL/Data System // Proc. 7th Int. Conf. on Very Large Data Bases. — Cannes, France. — September 1981.
Обсуждаются уроки, полученные в результате использования прототипа системы System R, а также описывается эволюция этого прототипа до первого семейства реляционных продуктов DB2 компании IBM, а именно — SQL/DS (переименованного впоследствии в "DB2 for VM and VSE").
4.15. Date C.J. A Critique of the SQL Database Language // ACM SIGMOD Record. — November 1984. — 14, № 3. (Переиздано: C.J. Date. Relational Database: Selected Writings. — Reading, Mass.: Addison-Wesley, 1986.) Как уже подчеркивалось в этой главе, язык SQL далек от совершенства. В статье представлен критический анализ его принципиальных недостатков (в основном исходя из требований к формальному компьютерному языку вообще, а не из требований к языку баз данных).
Примечание. Некоторые критические замечания из этой статьи не относятся к стандарту SQL: 1999.
4.16. Date C.J. What's Wrong with SQL? // Date C.J. Relational Database Writings 1985Reading, Mass.: Addison-Wesley, 1990.
Обсуждаются некоторые недостатки языка SQL в дополнение к описанным в [4.15] под заголовками "Недостатки собственно языка SQL", "Недостатки стандарта SQL" и "Переносимость приложений".
Примечание. Некоторые критические замечания из этой статьи не относятся к стандарту SQL: 1999.
4.17. Date C.J. SQL Dos and Don'ts // Date C.J. Relational Database Writings 1985-1989. — Reading, Mass.: Addison-Wesley, 1990.
В статье предложены некоторые практические советы по использованию языка SQL, что позволяет избежать потенциальных ловушек, вызванных недостатками этого языка, которые описаны в [4.15], [4.16], [4.16], и получить максимальные преимущества по производительности, переносимости, обеспечению связи и т.п.
4.18. Date C.J. How We Missed the Relational Boat // Date C.J. Relational Database Writings 1991-1994. — Reading, Mass.: Addison-Wesley, 1995.
Краткое заключение о недостатках языка SQL, имеющих отношение к поддержке (или отсутствию таковой) некоторых элементов реляционной модели: структурных элементов, средств обработки и обеспечения целостности.
4.19. Date C.J. Grievous Bodily Harm (в двух частях) // DBP&D. — May 1998. — 11, № 5 и DBP&D. — June 1998. — 11, № 6. См. также Fifty Ways to Query // Web-узел DBP&D www. dbpd. com. — July 1998.
Язык SQL чрезмерно избыточен в том смысле, что если не все, то большинство простых запросов могут быть выражены различными способами. В статьях это показано на примерах; в них также обсуждаются возможные следствия избыточности языка SQL. В частности показано, что конструкции GROUP BY, HAVING и переменные области значений можно с большой пользой исключить из языка, не потеряв при этом никаких функциональных возможностей (то же самое справедливо и в отношении конструкции "IN ").
Примечание. Все перечисленные выше конструкции поясняются в главе 8, раздел 8.6.
160 Часть I. Основные понятия 4.20. Date C.J., Darwen H. A Guide to the SQL Standard (4th edition). — Reading, Mass.:
Addison-Wesley, 1997.
Полное руководство по стандарту SQL (версии, выпущенной в 1992 году), включая описание средств SQL/CLI (по состоянию на 1995 год), SQL/PSM (по состоянию на 1996 год) и предварительный анализ спецификации SQL: 1999. В частности, в приложении D к этой книге приведены сведения о "многих аспектах стандарта, которые на сегодняшний день определены неадекватно или даже неверно". Большинство недостатков, указанных в этом приложении, обнаруживаются и в версии 4.21. Date C.J. and Colin J.W. A Guide to DB2 (4th edition). — Reading, Mass.: Addison-Wesley, В книге дается обширный и детальный обзор СУБД DB2 компании IBM (по состоянию на 1993 год) и некоторых ее сопутствующих продуктов. СУБД DB2 также была основана на системе System R, хотя и не в такой степени, как SQL/DS [4.14].
4.22. Fishman N. SQL du Jour. // DBP&D. - October 1997. - 10, № 10.
Оставляющий грустное впечатление обзор некоторых несовместимостей, обнаруженных в продуктах SQL, которые были объявлены как "поддерживающие стандарт SQL".
4.23. International Organization for Standardization (ISO): Information Technology — Database Languages— SQL, Document ISO/IEC 9075:1999. Примечание: см. также Оригинальное определение стандарта ISO/ANSI SQL: 1999 (специалисты называют его также ISO/IEC9075 или просто ISO 9075). Первоначальный документ, состоящий из одной части, был со временем расширен до неограниченной серии, состоящей из отдельных частей (ISO/IEC 9075-1, 9075-2 и т.д.). Ко времени выпуска данной книги были определены следующие части.
Часть 1. Структура (SQL/Framework).
Часть 2. Основы (SQL/Foundation).
Часть 3. Интерфейс на уровне вызова (SQL/CLI).
Часть 4. Постоянные хранимые модули (SQL/PSM).
Часть 5. Связь с базовым языком (SQL/Bindings).
\ Часть 6. Отсутствует.
Часть 9. Управление внешними данными (SQL/MED).
Часть 10. Связь с объектным языком (SQL/OLB).
Как указано выше в этой главе, следующая версия стандарта должна была быть официально утверждена в 2003 году и предполагалось, что в состав этого документа войдут описанные ниже изменения.
Материал из части 5 будет перенесен в часть 2, а часть 5 будет удалена.
Материал из части 2, содержащий определение стандартного каталога базы данных ("информационной схемы"), будет перемещен в новую часть 11, Появится новая часть 13, "Подпрограммы и типы Java (SQL/JRT)", которая будет использоваться для стандартизации результатов дальнейшей интеграции В новой части 14, "Спецификации, касающиеся XML (SQL/XML)", будут стандартизированы средства, обеспечивающие взаимодействие SQL и XML Примечание. Стоит упомянуть, что язык SQL часто называют международным стандартом "реляционных" баз данных, но в документе стандарта это не утверждается; в действительности в документе вообще не используется термин relation (отношение)! (Тем не менее, как указано в одной из приведенных выше сносок, в нем не упоминается и термин база данных.) 4.24. International Organization for Standardization (ISO) : (ISO Working Draft) — Database Language SQL— Technical Corrigendum 5, Document ISO/IEC JTC1/SC32/WG (December 2, 2001).
Содержит большое количество исправлений и дополнений к спецификации [4.22].
4.25. Raymond A.L. and Daudenarde J.J. SQL and its Applications. — Englewood Cliffs, N.J.:
Prentice-Hall, 1991.
Книга о языке SQL, содержащая практические советы и инструкции (почти половина книги посвящена подробному обсуждению ряда примеров применения прак-.
тических приложений).
4.26. Raymond A.L. and Nilsson J.F. An Access Specification Language for Relational Data Base System // IBM J.R&D. - May 1979. - 23, № 3.
В публикации подробно рассматриваются вопросы, связанные с техникой компиляции в системе System R [4.12], [4.27]. Для любого данного оператора SQL оптимизатор системы генерирует программу на внутреннем языке ASL (Access Specification Language — язык спецификации доступа). Этот язык используется как интерфейс между оптимизатором и генератором объектного кода. (Генератор объектного кода, как следует из его названия, преобразует программу на языке ASL в машинный код.) Язык ASL состоит из операторов типа scan (просмотр) и insert (вставка), применяемых к таким объектам, как индексы и хранимые файлы. Язык создавался, чтобы весь процесс трансляции можно было сделать более управляемым. Это достигается путем разбиения процесса трансляции на множество четко определенных подпроцессов.
4.27. Raymond A.L. and Bratford W.W. Compilation of High-Level Data Language. IBM Research Report RJ2598. — August 1979.
В системе System R впервые была применена идея компиляции запросов перед их выполнением с последующей автоматической рекомпиляцией запросов, если физическая структура базы данных значительно изменилась на каком-то промежуточном этапе. В этой статье подробно описывается механизм компиляции 162 Часть I. Основные понятия и рекомпиляции, но не затрагиваются вопросы оптимизации (эта актуальная тема подробно рассматривается в [18.33]).
4.28. Melton J., Simon A.R.: SQL: 1999 — Understanding Relational Components. —San Francisco, Calif.: Morgan Kaufmann, 2002.
Руководство по стандарту SQL: 1999 (представлены только основы, а описание дополнительных возможностей отложено до выпуска книги [26.32]). Ко времени выхода [14.28] из печати один из авторов этого руководства (Melton) был редактором спецификации стандарта SQL.
4.29. Rozenshtein D., Abramovich A., Birger E. Optimizing Transact-SQL: Advanced Prog ramming Techniques. — Fremont, Calif.: SQL Forum Press, 1995.
Язык Transact-SQL — это диалект языка SQL, который поддерживается такими продуктами, как Sybase и SQL Server. В этой книге рассматривается ряд приемов программирования для языка Transact-SQL, которые основаны на использовании характеристических функций (определенных авторами как "средства, позволяющие программистам кодировать логические условия в виде... выражений в предложениях SELECT, WHERE, GROUP BY И SET"). ХОТЯ ЭТИ идеи выражены в терминах языка Transact-SQL, на самом деле они имеют более широкую область применения.
Примечание. Необходимо отметить, что слово "optimizing", которое входит в название книги, не имеет отношения к оптимизатору СУБД. Напротив, здесь подразумевается оптимизация, которая может выполняться самими пользователями
РЕЛЯЦИОННАЯ МОДЕЛЬ
Нет ни малейшего сомнения в том, что основой современной технологии баз данных является реляционная модель; именно благодаря наличию этой основы данная отрасль знаний становится наукой. Поэтому, по определению, любая книга с описанием основ технологии баз данных, которая не включает исчерпывающего описания реляционной модели, является неполной.Аналогичным образом, любые попытки представить себя как эксперта в области баз данных вряд ли будут оправданы, если претендент на это звание не понимает глубоко реляционную модель. Спешим добавить, что этот материал не слишком "сложен", даже напротив, но еще раз отметим, что он представляет собой основу и останется таковой в обозримом будущем.
Как было описано в главе 3, реляционная модель распространяется на три принципиальных аспекта организации данных: структура данных, манипулирование данными и целостность данных. В этой части книги последовательно рассматриваются все три аспекта.
В главах 5 и 6 описана структура данных (в главе 5 речь идет о типах, а в об отношениях).
Главы 7 и 8 посвящены вопросам манипулирования данными (в главе ривается реляционная алгебра, а в главе 8 — реляционное исчисление).
В главе 9 дано описание темы целостности данных.
Наконец, в главе 10 рассматривается такая важная тема, как представления.
Примечание. Следует добавить, что это деление реляционной модели на три части, хотя и очень удобное на высоком концептуальном уровне, становится менее четким с того момента, когда начинается анализ более конкретных вопросов. В действительности, как вскоре станет очевидно, отдельные компоненты этой модели тесно взаимосвязаны и во многом зависят друг от друга, поэтому в целом невозможно (даже в принципе) удалить какой-то отдельный компонент, не разрушив всю модель. Одним из следствий этого факта является то, что главы 5—10 включают многочисленные перекрестные ссылки друг на друга.
164 Часть II. Реляционная модель Важно также понять, что реляционная модель не остается неизменной — с годами она постоянно развивалась и расширялась и эта тенденция сохраняется1. Текст, приведенный в этих главах, отражает современные взгляды самого автора и других исследователей, работающих в этой области (как указано в предисловии, особое влияние на эти взгляды оказали идеи Третьего Манифеста [3.3]). Рассматриваемый подход представляется весьма обоснованным, даже окончательно сложившимся (ко времени выхода данной книги из печати), хотя, безусловно, в этой части книги он представлен не как описание текущих исследований, а как учебный материал, но читатель не должен думать, что эта область знаний не имеет дальнейших перспектив развития.
Еще раз отметим, что реляционную модель понять не так уж трудно, но это — теория, а большинство теорий обладает своей собственной специальной терминологией, и реляционная модель (по причинам, уже описанным в разделе 3.3) не является исключением из этого правила. Поэтому вполне естественно, что в данной части книги будет использоваться специальная терминология. Тем не менее, трудно оспаривать тот факт, что эта терминология на первых порах может оказаться немного обескураживающей и действительно способна стать барьером для понимания. (Этот факт особенно достоин сожаления, поскольку идеи, лежащие в основе реляционной модели, действительно не так уж сложны для понимания.) Таким образом, если читатель будет испытывать затруднения в понимании изложенного ниже материала, просим его сохранять терпение; ему вскоре станет очевидно, что изложенные здесь концепции вполне понятны, как только он ознакомится с терминологией.
Следует также добавить, что главы в этой части являются весьма объемными (на их основе вполне можно выпустить отдельную книгу). Но большой объем этих глав отражает лишь важность рассматриваемой темы! Было бы вполне возможно представить краткий обзор этой темы всего лишь на одной или двух страницах. И действительно, одно из важных преимуществ реляционной модели состоит в том, что ее основные идеи можно объяснить и понять очень легко. Тем не менее, краткое изложение не соответствует важности рассматриваемого предмета и не позволяет показать широкую область применения реляционной модели. Итак, значительный объем этой части книги следует рассматривать не как свидетельство значительной сложности модели, а как признак ее важности и ее успешного применения в качестве основы для многочисленных перспективных разработок. Усилия, затраченные читателем на полное понимание этого материала, многократно окупятся благодаря его дальнейшей успешной деятельности в области баз данных.
Наконец, кратко коснемся языка SQL. В части I этой книги уже было сказано, что SQL является стандартным языком реляционных баз данных и его поддерживает почти любой продукт категории баз данных, имеющийся на рынке (точнее, поддерживает некоторый диалект этого языка — см. [4.22]). Вследствие этого ни одна книга по современным базам данных не будет полной без исчерпывающего описания SQL. Поэтому в приведенных ниже главах, посвященных различным аспектам реляционной модели, рассматриваются также соответствующие средства SQL, в той степени, в какой они относятся к данной теме (при этом описание основано на материале главы 4, в которой приведены основные концепции языка SQL).
В этом отношении она напоминает математику (область математических знаний также не является застывшей, а развивается со временем); в действительности, сама реляционная модель может рассматриваться как малая ветвь математики.
Типы 5.1. Введение 5.2. Определение значений и переменных 5.3. Определения типов и форматов представления 5.4. Определение типа 5.5. Операторы 5.6. Генераторы типов 5.7. Средства SQL 5.8. Резюме Список литературы 5.1.ВВЕДЕНИЕ Примечание. При первом прочтении книги читатель может пожелать ознакомиться с этой главой лишь кратко. Сама эта глава по праву занимает место, отведенное ей в данной части, но значительный объем представленного в ней материала фактически не потребуется до главы 20 части V и глав 25- части VI.
Понятие типа данных (или сокращенно типа) является фундаментальным;
каждое значение, каждая переменная, каждый параметр, каждый оператор, предназначенный только для чтения, и особенно каждый реляционный атрибут относится к тому или иному типу. Так что же такое тип? Кроме всего прочего, он представляет собой множество значений. К примерам таких типов относятся INTEGER (множество всех целых чисел), CHAR (множество всех символьных строк), s# (множество всех номеров поставщиков) и т.д. Поэтому, например, говоря о том, что переменная отношения s с данными о поставщиках имеет атрибут STATUS типа INTEGER, МЫ ПОД ЭТИМ подразумеваем, что значениями этого атрибута являются целые числа и ничего кроме целых чисел.
Примечание. Из этого непосредственно следуют два изложенных ниже вывода.
166 Часть II. Реляционная модель Первым является то, что типы можно также называть доменами, особенно когда речь идет о реляционной модели; фактически автор сам использовал последний термин в предыдущих изданиях настоящей книги, но теперь предпочитает термин Второй вывод может служить предостережением: автор пытается сохранить в этой части книги разумную точность. Поэтому он не утверждает, например, что тип INTEGER представляет собой множество всех возможных целых чисел, а должен фактически сделать оговорку, что это— множество всех целых чисел, которые могут быть представлены в рассматриваемой компьютерной системе (поскольку, безусловно, есть такие целые числа, которые превышают возможности представления в любой компьютерной системе). Вполне очевидно, что аналогичное уточнение относится ко многим последующим утверждениям и примерам, приведенным в этой главе; в связи с этим автор не будет явно подчеркивать указанную особенность типов, но просит читателя всегда учитывать данное предостережение.
Любой отдельно взятый тип является либо определяемым системой (т.е. встроенным), либо определяемым пользователем. В данной главе предполагается, что из трех типов, упомянутых выше, INTEGER И CHAR определены системой, a s# определен пользователем. Но основой для объявления реляционных атрибутов (а также переменных, параметров и операторов, предназначенных только для чтения, — см. раздел 5.2) может стать любой тип, независимо от того, определен ли он системой или пользователем.
С любым конкретным типом связано множество операторов, которые могут с полным правом применяться к значениям рассматриваемого типа; это означает, что операции со значениями указанного типа могут выполняться исключительно с помощью операторов, определенных для этого типа (выражение "определен для этого типа" означает именно то, что рассматриваемый оператор имеет параметр, который объявлен как относящийся к этому типу). Например, в случае определяемого системой типа INTEGER используются описанные ниже операторы.
В системе предусмотрены операторы для сравнения целых чисел,"=", "" для типа QTY.
OPERATOR GT ( Q1 QTY, Q2 QTY ) RETURNS BOOLEAN ;
RETURN ( THE_QTY ( Ql ) > THE_QTY ( Q2 ) ) ; END OPERATOR ;
Здесь в выражении в составе оператора RETURN используется встроенный оператор ">" для типа INTEGER. И в данном случае подразумевается, что начиная с этого момента в качестве данного оператора может использоваться обычное инфиксное обозначение, причем не только для типа QTY, но и для всех так называемых порядковых типов. (По определению порядковым типом называется тип, к которому применим оператор ">". Простым примером непорядкового типа является POINT.) Наконец, ниже приведен пример определения оператора обновления (все предыдущие примеры относились к типу операторов, предназначенных только для чтения, в которых не допускается обновлять что-либо, за исключением, возможно, локальных переменных)8. Вполне очевидно, что в этом определении вместо спецификации RETURNS применяется спецификация UPDATES; операторы обновления не возвращают значение и должны вызываться с помощью явно заданных операторов CALL [3.3].
OPERATOR REFLECT ( Р POINT ) UPDATES
Оператор REFLECT по сути перемещает точку, обозначенную своими декартовыми координатами (х,у), в противоположную позицию (-х, -у); он выполняет это действие путем соответствующего обновления переданных ему фактических параметров с координатами точки. Обратите внимание на то, что в этом примере используются псевдопеременные ТНЕ_. Псевдопеременная ТНЕ_ представляет собой вызов оператора ТНЕ_ в целевой позиции (в частности, слева от оператора присваивания). При таком вызове Операторы, предназначенные только для чтения, и операторы обновления часто называют также, соответственно, наблюдателями и модификаторами, особенно в объектных системах (см. главу 25). Для обозначения операторов, предназначенных только для чтения, применяется еще один синоним — функция (и этот термин время от времени используется в указанном значении и в данной книге).180 Часть II. Реляционная модель фактически обозначается заданный компонент (соответствующего возможного представления) фактического параметра (а не просто возвращается его значение). Например, в определении оператора REFLECT следующий оператор присваивания фактически присваивает значение компоненту X (декартового возможного представления) переменной формального параметра, соответствующей параметру Р. Безусловно, любой формальный параметр, который необходимо обновить с помощью оператора обновления (в частности, путем присваивания значения псевдопеременной ТНЕ_), должен быть задан именно как переменная, а не в виде некоторого более общего выражения.
Псевдопеременные могут быть вложенными, как показано ниже.
VAR LS LINESEG ;
ТНЕ_Х ( THE_BEGIN ( LS ) ) := 6.5 ;Но следует отметить, что в принципе псевдопеременные ТНЕ_ не являются строго необходимыми. Еще раз рассмотрим следующий оператор присваивания.
Этот оператор присваивания, в котором используется псевдопеременная, является логически эквивалентным оператору, приведенному ниже, в котором таковая не используется.
. Р := CARTESIAN ( - ТНЕ_Х ( Р ), THE_Y ( Р ) ) ;
Аналогичным образом, следующий оператор присваивания ТНЕ_Х ( THE_BEGIN ( LS ) ) := б.5 ;
является логически эквивалентным приведенному ниже.
LS := LINESEG ( CARTESIAN ( 6.5, Иными словами, псевдопеременные как таковые не являются строго необходимыми для поддержки той разновидности обновления на уровне компонентов, которая рассматривается в этом разделе. Но интуитивно подход с использованием псевдопеременных кажется более привлекательным, чем показанный выше альтернативный подход (к тому же он может рассматриваться как более упрощенный вариант этого альтернативного подхода); кроме того, он также обеспечивает более высокую степень невосприимчивости к изменениям в синтаксисе соответствующего селектора. (К тому же может оказаться, что его проще реализовать наиболее эффективно.) Коснувшись темы упрощений, следует указать, что единственным оператором обновления, который в принципе необходим, является оператор присваивания (":=");
все остальные операторы обновления могут быть определены исключительно в терминах присваивания (как уже фактически было показано в главе 3, особенно на примере реляционных операторов обновления). Но нужна также поддержка для множественной формы присваивания, которая позволяет "одновременно" выполнить любое количество отдельных присваиваний [3.3]. Например, можно заменить два оператора присваивания в определении оператора REFLECT следующим оператором множественного присваивания.
ТНЕ_Х ( Р ) := - ТНЕ_Х (Р) (В этой конструкции заслуживает особого внимания разделитель в виде запятой.) Этот оператор имеет следующую семантику: во-первых, вычисляются все исходные выражения с правых сторон операторов присваивания; во-вторых, после этого все отдельные операторы присваивания выполняются в той последовательности, в которой они записаны9.
Примечание. Поскольку операция множественного присваивания рассматривается как одна операция, "в процессе" подобного присваивания проверка целостности не производится; в действительности этот факт является одной из основных причин, по которым мы стремимся в первую очередь обеспечить поддержку множественного присваивания. Дополнительные сведения по этой теме приведены в главах 9 и 16.
Наконец, должна быть предусмотрена возможность избавиться от некоторого оператора, если он в дальнейшем не потребуется, например, как показано ниже.
DROP OPERATOR REFLECT ;
Данный конкретный оператор должен быть определяемым пользователем, а не встро-.енным.
Преобразования типов Еще раз рассмотрим следующее определение типа.
TYPE S# POSSREP { CHAR } ;
Здесь по умолчанию возможное представление имеет унаследованное имя S# и поэтому такое же имя имеет и соответствующий оператор-селектор. Таким образом, показанный ниже вызов селектора является допустимым.
(Этот вызов возвращает определенный номер поставщика.) Поэтому можно сделать вывод, что данный селектор s# может неформально рассматриваться как оператор преобразования типа, который преобразует символьные строки в номера поставщиков.
Аналогичным образом, селектор Р# может рассматриваться как оператор преобразования, который преобразует символьные строки в номера деталей, а селектор QTY — как оператор преобразования, преобразующий целые числа в значения количества, и т.д.
На основании таких же рассуждений операторы ТНЕ_ могут рассматриваться как операторы, осуществляющие преобразование типа в противоположном направлении.
Например, еще раз вернемся к определению типа WEIGHT, приведенному в начале раздела 5.4.
Это определение требует некоторого уточнения в том случае, если два или больше отдельных операторов присваивания относятся к одной и той же целевой переменной. Подробные сведения о такой ситуации выходят за рамки данной книги; достаточно отметить, что подобные операции должны быть тщательно определены, с тем чтобы достигался требуемый результат в тех случаях, когда (как в рассматриваемой примере) фактически различные отдельные операторы присваивания обновляют отдельные части одной и той же целевой переменной (этот конкретный случай является особенно важным).
182 Часть //. Реляционная модель TYPE WEIGHT POSSREP { D DECIMAL (5,1) Если w относится к типу WEIGHT, то выражение фактически преобразует вес, обозначенный переменной w, в десятичное число DECIMAL( 5, 1 ).
Итак, в разделе 5.2 было указано, что источник и цель в операторе присваивания должны относиться к одному и тому же типу и что к одинаковому типу должны относиться операнды в операции сравнения на равенство. Но в некоторых системах соблюдение этих правил непосредственно не предписано, поэтому в таких системах можно, например, сформировать запрос на проведение сравнения между номером детали и символьной строкой (допустим, в конструкции WHERE), как показано ниже.
В данном случае левый операнд относится к типу Р#, а правый — к типу CHAR, поэтому по указанным выше соображениям попытка провести такое сравнение должна окончиться неудачей из-за ошибки, связанной с несоответствием типов (фактически ошибка, связанная с несоответствием типов, должна обнаруживаться на этапе компиляции).
Но концептуально здесь происходит именно то, что система определяет возможность использования "оператора преобразования" Р# (иными словами, селектора Р#) для преобразования операнда операции сравнения типа CHAR в тип Р#, и поэтому фактически преобразует эту операцию сравнения в представленную ниже.
После этого операция сравнения становится допустимой.
Неявный вызов оператора преобразования подобным образом называется приведением типа (coercion). Но хорошо известно, что непродуманное использование приведения типа может стать причиной возникновения ошибок в программе. По этой причине мы в данной книге придерживаемся консервативной позиции, согласно которой неявно заданные операции приведения типа не допускаются: операнды всегда должны относиться к соответствующим типам, а не просто быть приводимыми к этим типам.
Безусловно, автор допускает возможность определения операторов преобразования типа (или операторов CAST, как они обычно называются) и явного их вызова в случае необходимости, например, как показано ниже.
CAST_AS_CHAR ( 53 0.00 ) Как уже было сказано, селекторы (по меньшей мере, те из них, которые принимают только один параметр) могут рассматриваться как своего рода операторы явного преобразования.
Итак, читатель к этому времени вполне мог прийти к заключению, что все сказанное здесь относится к подходу, известному в кругу пользователей языков программирования как строгая типизация. Различные авторы приводят немного разные определения для этого понятия, но в том виде, в каком оно определено в данной книге, это понятие означает, кроме всего прочего, что каждое значение имеет тип и при каждой попытке выполнить некоторую операцию система проверяет, имеют ли операнды типы, допустимые для использования в данной операции. Например, рассмотрим следующие выражения.
WEIGHT + QTY /* Суммирование веса детали с количеством деталей в поставке */ WEIGHT * QTY /* Умножение веса детали на количество деталей Первое из этих выражений не имеет смысла, и система должна отвергнуть попытку его выполнить. С другой стороны, второе выражение вполне обосновано: оно обозначает общий вес всех деталей, которые входят в данную поставку. Поэтому операторы, которые могут быть определены для весов и количеств, применяемых в сочетании, должны, по всей видимости, включать"*", но не "+".
Ниже приведено еще несколько примеров, в которых на этот раз рассматриваются операции сравнения.
WEIGHT > QTY EVEN > (Во втором примере предполагается, что операнд EVEN относится к типу четного целого числа EVEN_INTEGER, а операнд ODD — к типу нечетного целого числа ODD_INTEGER, причем семантика этих типов является очевидной.) Поэтому и в данном случае первое выражение не имеет смысла, а второе действительно имеет смысл. Поэтому операторы, которые могут быть определены для сочетаний весов и количеств, по-видимому, не должны включать ">", а для четных и нечетных целых чисел применение оператора ">" является вполне допустимым10. (Что касается проблемы определения того, какие операторы являются допустимыми для тех или иных типов, следует отметить, что по традиции в большей части литературы по базам данных, включая первые несколько изданий настоящей книги, рассматривались только операторы сравнения и игнорировались все прочие операторы, такие как"+" и "*".) Заключительные замечания Изучение вопроса о полной поддержке операторов, соответствующих требованиям, изложенным в настоящем разделе, приводит к нескольким важным выводам, которые кратко изложены ниже.
Первый и наиболее важный вывод состоит в том, что система должна иметь ин формацию о том, какие именно выражения являются допустимыми и каким быть тип результата для каждого такого допустимого выражения.
Это также означает, что общая коллекция типов для конкретной базы данных должна представлять собой замкнутое множество; из этого следует вывод, что тип результата каждого допустимого выражения должен быть типом, известным сис теме. В частности, следует отметить, что это замкнутое множество типов должно На практике типы EVEN_INTEGER и ODD_INTEGER вполне могли быть определены как подтипы типа INTEGER, и в этом случае оператор ">", вероятно, был бы унаследован от этого последнего типа (см. главу 20).
184 Часть II. Реляционная модель также включать логический тип (или истинностное значение), поскольку в ином случае ни один оператор сравнения не будет допустимым!
В частности, из того факта, что системе должен быть известен тип результата каждого допустимого выражения, следует, что системе должно быть известно, какие операции присваивания и какие операции сравнения являются допустимыми.
Завершаем этот раздел ссылкой на изложенный ниже материал. Перед этим уже было указано, что понятия, называемые по традиции в сообществе пользователей реляционных систем доменами, в действительности являются типами данных, определяемыми системой или пользователем, имеющими произвольную внутреннюю сложность, значениями которых можно оперировать исключительно с помощью операторов, определенных для рассматриваемого типа (и физическое представление которых по этой причине должно быть скрыто от пользователя). Итак, если мы теперь обратим наше внимание на объектные системы, то обнаружим, что наиболее фундаментальная концепция объекта, класс объекта, в действительности представляет собой тип данных, определяемый системой или пользователем, имеющий произвольную внутреннюю сложность, значениями которого можно оперировать исключительно с помощью операторов, определенных для рассматриваемого типа (и физическое представление которых по этой причине должно быть скрыто от пользователя).... Иными словами, домены и классы объектов представляют собой одно и то же! И поэтому нам остается только соединить эти две технологии (отношения и объекты). Этот важный вопрос исследуется в главе 26.
5.6. ГЕНЕРАТОРЫ-ТИПОВ Теперь обратимся к изучению типов, которые не определены с помощью оператора TYPE, а получены путем вызова некоторого генератора типа (type generator). В общем, генератор типа — это просто особый вид оператора; его специфика состоит в том, что он возвращает тип, скажем, вместо простого скалярного значения. Например, в обычном языке программирования можно применить следующую конструкцию.
VAR SALES ARRAY INTEGER [12] ;
Эта конструкция позволяет определить переменную SALES, допустимыми значениями которой являются одномерные массивы из 12 целых чисел. В этом примере выражение ARRAY INTEGER [12] может рассматриваться как вызов генератора типа ARRAY, который возвращает массив конкретного типа. Данный конкретный тип массива является сгенерированным типом. Из этого следуют приведенные ниже выводы.
1. Генераторы типов известны в литературе под многими разными названиями, включая конструкторы типов, параметризованные типы, полиморфные типы, шаблоны типов и универсальные типы. В данной книге принят термин генератор 2. Сгенерированные типы являются вполне допустимыми типами и могут приме няться везде, где допустимо использование обычных "несгенерированных" типов.
Например, может быть определена некоторая переменная отношения, имеющая определенный атрибут типа ARRAY INTEGER [ 12 ]. В отличие от этого, сами ге нераторы типов как таковые не являются типами.
3. Большинство сгенерированных типов (но не все) являются именно нескалярными типами (одним из характерных примеров могут служить типы массивов). Поэтому в разделе 5.4 мы обещали показать, а здесь показали, как могут быть определены нескалярные типы.
Примечание. Хотя и может существовать возможность определять нескалярные типы, не вызывая непосредственно некоторый генератор типа, в данной книге такая возможность больше не рассматривается.
4. Для полной ясности отметим, что мы рассматриваем сгенерированные типы именно как типы, определяемые системой, поскольку для их получения требуется вызвать генератор типа, определяемый системой.
Примечание. Фактически здесь допущено некоторое упрощение. В частности, мы не исключаем возможности для пользователей определять свои собственные генераторы типов. Но такая возможность в данной книге больше не рассматривается.
Итак, сгенерированные типы, безусловно, имеют возможные представления (сокращенно possrep — possible representation), которые, вполне очевидно, являются производными, во-первых, от универсального возможного представления, применяемого к рассматриваемому генератору типа, и, во-вторых, от конкретного возможного представления (представлений) видимого пользователю компонента (компонентов) конкретного рассматриваемого сгенерированного типа. Например, в случае генератора типа ARRAY INTEGER [ 12 ] имеют место описанные ниже особенности.
Должно существовать некоторое универсальное возможное представление, определенное для одномерных массивов в целом, возможно, как непрерывная последовательность элементов массива, которые могут быть определены с помощью индексов в диапазоне от нижнего до верхнего (где нижний и верхний индексы обозначают допустимые границы; в данном примере 1 и 12).
Видимые пользователю компоненты представляют собой именно те 12 элементов массива, которые были только что упомянуты, и они имеют то возможное представление (представления), которое определено для типа INTEGER.
Аналогичным образом, должны быть предусмотрены операторы, предоставляющие необходимые функциональные возможности селектора и оператора ТНЕ_. Например, выражение которое по сути представляет собой литерал с определением массива, может использоваться для задания конкретного значения типа ARRAY INTEGER [ 12 ] (иными словами, "для применения функциональных средств селектора"). Аналогичным образом, следующее выражение SALES [3] может использоваться для доступа к третьему компоненту (т.е. к третьему элементу массива) того значения массива, которое оказалось текущим значением переменной массива SALES (иными словами, "для применения функциональных средств оператора ТНЕ_").
Это выражение может также служить в качестве псевдопеременной.
186 Часть П. Реляционная модель Применимы также операторы присваивания и сравнения для проверки на равенство.
Например, ниже приведен допустимый оператор присваивания.
А в данном случае определен допустимый оператор проверки на равенство.
Примечание. Любой конкретный генератор типа может также иметь множество связанных с ним универсальных ограничений и операторов (они являются универсальными в том смысле, что рассматриваемые ограничения и операторы применяются к любому конкретному типу, полученному с помощью вызова рассматриваемого генератора типа).
Например, в случае генератора типа ARRAY имеют место описанные ниже особенности.
Может быть предусмотрено такое универсальное ограничение, что нижний индекс lower не должен быть больше верхнего индекса upper.
Может быть предусмотрен универсальный оператор "обращения" REVERSE, который принимает в качестве входных данных произвольный одномерный массив и возвращает в качестве выходных данных другой такой же массив, содержащий элементы первого массива в обратном порядке.
(В действительности, упомянутые выше селекторы, операторы ТНЕ_, а также операторы присваивания и сравнения на равенство также фактически происходят от некоторых универсальных операторов.) В заключение отметим, что двумя генераторами типов, которые имеют особую важность в мире реляционных баз данных, являются TUPLE И RELATION. ЭТИ операторы подробно рассматриваются в следующей главе.
5.7. СРЕДСТВА SQL Встроенные типы В языке SQL предусмотрены перечисленные ниже встроенные типы, имена которых в основном говорят сами за себя.