Замечание: эти ограничения
являются очень сильными. В
реализациях они могут быть
ослаблены. Но если
стремиться к мобильности,
не следует пользоваться
расширенными
возможностями.Если в
списке выборки
спецификации запроса
имеется хотя бы одно
арифметическое выражение,
состоящее не из одной
спецификации столбца, или
если имя хотя бы одного
столбца участвует в списке
выборки более одного раза,
определение представления
должно содержать список
имен столбцов
представляемой таблицы.
Более просто, нужно явно
именовать столбцы
представляемой таблицы,
если эти имена не
наследуются от столбцов
таблиц раздела FROM
спецификации запроса.
Требование WITH CHECK OPTION в
определении представления
имеет смысл только в
случае определения
изменяемой представляемой
таблицы, которая
определяется
спецификацией запроса,
содержащей раздел WHERE. При
наличии этого требования
не допускаются изменения
представляемой таблицы,
приводящие к появлению в
базовых таблицах строк, не
видимых в представляемой
таблице (т.е. таких строк,
которые не удовлетворяют
условию поиска раздела WHERE
спецификации запроса).
Если WITH CHECK OPTION в
определении представления
отсутствует, такой
контроль не производится.
2.4.4 Определение
привилегий
В соответствии с
идеологией языка SQL
контроль прав доступа
данного пользователя к
таблицам БД производится
на основе механизма
привилегий. Фактически,
этот механизм состоит в
том, что для выполнения
любого действия над
таблицей пользователь
должен обладать
соответствующей
привилегией (реально все
возможные действия
описываются фиксированным
стандартным набором
привилегий). Пользователь,
создавший таблицу,
автоматически становится
владельцем всех возможных
привилегий на выполнение
операций над этой
таблицей. В число этих
привилегий входит
привилегия на передачу
всех или некоторых
привилегий по отношению к
данной таблице другому
пользователю, включая
привилегию на передачу
привилегий. Иногда
поддерживается и обратная
операция изъятия
привилегий от
пользователя, ранее их
получившего.
В SQL/89 определяется
упрощенная схема
механизма привилегий.
Во-первых, "раздача"
привилегий возможна
только при определении
таблицы. Во-вторых,
пользователь, получивший
некоторые привилегии от
других пользователей,
может передать их дальше
только при определении
схемы.
Оператор определения
привилегий обладает
следующим синтаксисом:
<privilege definition> ::=
GRANT <privileges> ON <table name>
TO <grantee> [{,<grantee>}...]
[WITH GRANT OPTION]
<privileges> ::=
ALL PRIVILEGES
| <action> [{,<action>}...]
<action> ::=
SELECT | INSERT | DELETE
| UPDATE [(<grant column list>)]
| REFERENCES [(<grant column list>]
<grant column list> ::=
<column name> [{,<column name>}...]
<grantee> ::=
PUBLIC | <authorization identifier>
Эти синтаксические
правила достаточно ясно
показывают смысл
механизма определения
привилегий в SQL/89. Заметим
лишь, что необходимо
обладать привилегией
REFERENCES по отношению к
указанным столбцам
таблицы T1, чтобы иметь
возможность при
определении таблицы T
специфицировать
ограничение по ссылкам
между этой таблицей и
существующей к этому
моменту таблицей T1.
Еще раз заметим, что, хотя
в общем смысле во всех
SQL-ориентированных СУБД
поддерживается механизм
защиты данных на основе
привилегий доступа,
реализации могут
различаться в деталях. Это
опять то место, которое
нужно локализовывать, если
стремиться к созданию
мобильной прикладной
системы.
2.5 Язык
модулей или встроенный SQL?
В стандарте SQL/89
определены два способа
взаимодействия с БД из
прикладной программы,
написанной на
традиционном языке
программирования (как мы
уже упоминали, SQL/89
ориентирован на
использование совместно с
языками Кобол, Фортран,
Паскаль и ПЛ/1, но в
реализациях обычно
поддерживается и язык Си).
Первый способ состоит в
том, что все операторы SQL, с
которыми может работать
данная прикладная
программа, собраны в один
модуль и оформлены как
процедуры этого модуля.
Для этого SQL/89 содержит
специальный подъязык -
язык модулей. При
использовании такого
способа взаимодействия с
БД прикладная программа
содержит вызовы процедур
модуля SQL с передачей им
фактических параметров и
получением ответных
параметров.
Второй способ состоит в
использовании так
называемого встроенного
SQL, когда с использованием
специального синтаксиса в
программу на традиционном
языке программирования
встраиваются операторы SQL.
В этом случае, с точки
зрения прикладной
программы, оператор SQL
выполняется "по
месту". Явная
параметризация операторов
SQL отсутствует, но во
встроенных операторах SQL
могут использоваться
имена переменных основной
программы, и за счет этого
обеспечивается связь
между прикладной
программой и СУБД.
Концептуально эти два
способа эквивалентны.
Более того, в стандарте
устанавливаются правила
порождения неявного
модуля SQL по программе со
встроенным SQL. Однако в
большинстве реализаций
операторы SQL, содержащиеся
в модуле SQL, и встроенные
операторы SQL
обрабатываются
существенно по-разному.
Модуль SQL обычно
компилируется отдельно от
прикладной программы, в
результате чего
порождается набор так
называемых хранимых
процедур (в стандарте этот
термин не используется, но
распространен в
коммерческих реализациях).
Т.е. в случае использования
модуля SQL компиляция
операторов SQL производится
единожды, и затем
соответствующие процедуры
сколько угодно раз могут
вызываться из прикладной
программы.
В отличие от этого, для
операторов SQL, встроенных в
прикладную программу,
компиляция этих
операторов обычно
производится каждый раз
при их использовании
(правильнее сказать, при
каждом первом
использовании оператора
при данном запуске
прикладной программы).
Конечно, пользователи не
обязаны знать об этом
техническом различии в
обработке двух видов
взаимодействия с СУБД.
Существуют и такие
системы, которые
производят одноразовую
компиляцию встроенных
операторов SQL и сохраняют
откомпилированный код. Но
все-таки лучше иметь это в
виду (в частности, при
компиляции оператора SQL
непосредственно перед его
выполнением вероятно
получение более
оптимального плана
выполнения оператора).
Приведем некоторые
соображения за и против
каждого из этих двух
способов. При
использовании языка
модулей текст прикладной
программы имеет меньший
размер, взаимодействия с
СУБД более локализованы за
счет наличия явных
параметров вызова
процедур. С другой стороны,
для понимания смысла
поведения прикладной
программы потребуется
одновременное чтение двух
текстов. Кроме того, как
кажется, синтаксис модуля
SQL может существенно
различаться в разных
реализациях. Встроенный SQL
предоставляет возможность
производства более
"самосодержащихся"
прикладных программ.
Имеется больше оснований
рассчитывать на простоту
переноса такой программы в
среду другой СУБД,
поскольку стандарт
встраивания более или
менее соблюдается.
Основным недостатком
является некоторый
PL-подобный вид таких
программ, независимо от
выбранного основного
языка. И конечно, нужно
учитывать замечания,
содержащиеся в предыдущих
абзацах.
Далее мы коротко опишем
язык модулей и правила
встраивания в
соответствии со
стандартом SQL/89 (еще раз
заметим, что формально
правила встраивания не
являются частью
стандарта).
2.5.1 Язык модулей
Структура модуля SQL в
стандарте SQL/89
определяется следующими
синтаксическими
правилами:
<module> ::=
<module name clause>
<language clause>
<module authorization clause>
[<declare cursor>...]
< procedure > ...
<module name clause> ::=
MODULE [<module name>]
<language clause> ::=
LANGUAGE { COBOL | FORTRAN | PASCAL | PLI }
<module authorization clause> ::=
<PRE>AUTHORIZATION <module
authorization identifier>
<module authorization identifier> ::=
<authorization identifier>
Существенно, что каждый
модуль SQL ориентирован на
использование в
программах, написанных на
конкретном языке
программирования. Если в
модуле присутствуют
процедуры работы с
курсорами (см. п. 2.6.1), то все
курсоры должны быть
специфицированы в начале
модуля. Заметим, что
объявление курсора не
погружается в какую-либо
процедуру, поскольку это
описательный, а не
выполняемый оператор SQL
(другими словами, все
курсоры будут
автоматически объявлены в
самом начале выполнения
прикладной программы,
связанной с модулем SQL).
2.5.1.1 ОПРЕДЕЛЕНИЕ
ПРОЦЕДУРЫ
Процедуры в модуле SQL
определяются в следующем
синтаксисе:
<procedure> ::=
PROCEDURE <procedure name>
<parameter declaration>...;
<SQL statement>;
<parameter declaration>::=
<parameter name> <data type>
| <SQLCODE parameter>
<SQLCODE parameter> ::=
SQLCODE
<SQL statement> ::=
<close statement>
| <commit statement>
| <delete statement positioned>
| <delete statement searched>
| <fetch statement>
| <insert statement>
| <open statement>
| <rollback statement>
| <select statement>
| <update statement positioned>
| <update statement searched>
Имена всех процедур в
одном модуле должны быть
различны. Любое имя
параметра, содержащегося в
операторе SQL процедуры,
должно быть
специфицировано в разделе
объявления параметров.
Число фактических
параметров при вызове
процедуры должно
совпадать с числом
формальных параметров,
указанных при ее
объявлении. Список
формальных параметров
каждой процедуры должен
содержать ровно один
параметр SQLCODE - код ответа
процедуры; возможные
значения кодов ответа
стандартизованы, но
некоторые (правильнее
сказать, абсолютное
большинство) из них
определяются в реализации.
2.5.2 Встроенный SQL
Поскольку в стандарте
SQL/89 не специфицированы
(даже в приложениях)
правила встраивания SQL в
язык Си, мы приведем только
общие синтаксические
правила встраивания,
используемые для любого
языка. Это поможет оценить
"уровень
стандартности"
конкретной реализации.
<embedded SQL statement> ::=
<SQL prefix>
{ <declare cursor>
| <embedded exception declaration>
| <SQL statement>}
[<SQL terminator>]
<SQL prefix> ::=
EXEC SQL
<SQL terminator> ::=
END EXEC | ;
<embedded SQL declare section> ::=
<embedded SQL begin declare>
[<host variable definition>...]
<embedded SQL end declare>
<embedded SQL begin declare> ::=
<SQL prefix> BEGIN DECLARE SECTION
[<SQL terminator>]
<embedded SQL end declare> ::=
<SQL prefix> END DECLARE SECTION
[<SQL terminator>]
<embedded variable name> ::=
:<host identifier>
<embedded exception declaration> ::=
WHENEVER <condition> <exception action>
<condition> ::=
SQLERROR | NOT FOUND
<exception action> ::=
CONTINUE | <go to>
<go to> ::=
{ GOTO | GO TO } <target>
<target> ::= :<host identifier> | <unsigned integer>
Встраиваемые операторы
SQL, включая объявления
курсора, а также разделы
объявления исключительных
ситуаций и переменных
основной программы, должны
быть окружены скобками EXEC
SQL и END EXEC. Объявление
курсора должно
встречаться текстуально
раньше любого оператора,
ссылающегося на этот
курсор. Все переменные
основной программы,
используемые во
встроенных операторах SQL,
должны быть объявлены в
текстуально
предшествующем этому
оператору разделе
объявления переменных
основной программы. При
этом синтаксис объявления
переменной соответствует
синтаксису основного
языка программирования, но
имени переменной
предшествует двоеточие.
Механизм обработки
исключительных ситуаций в
SQL/89 крайне прост (можно
сказать, примитивен). Можно
задавать реакцию на
возникновение двух видов
условий: SQLERROR - это условие
появления отрицательного
значения в переменной SQLCODE
после выполнения
встроенного оператора; NOT
FOUND - условие появления в
SQLCODE значения +100 (этот код
означает исчерпание
результирующего множества
запроса при его просмотре
через курсор). Реакция
может состоять в
выполнении безусловного
перехода на метку основной
программы (действие GO TO)
или отсутствовать
(действие CONTINUE).
Срабатывает тот оператор
определения реакции на
исключительную ситуацию,
который текстуально ближе
от начала программы к
данному оператору SQL.
Заметим, что во многих
реализациях
поддерживается два вида
кодов ответа при
выполнении операторов SQL
(встроенных или взятых из
модуля): через переменную
SQLCODE с кодами ответа,
представляемыми целыми
числами, и через
переменную SQLSTATE с кодами
ответа, которые кодируются
десятичными числами,
представленными в
текстовой форме. Имеется
тенденция к переходу на
использование только
механизма SQLSTATE, но в
стандартных реализациях
должен поддерживаться и
механизм SQLCODE.
2.6 Набор
операторов
манипулирования данными
В стандарте SQL/89
определен очень
ограниченный набор
операторов
манипулирования данными.
Их можно классифицировать
на группы операторов,
связанных с курсором;
одиночных операторов
манипулирования данными; и
операторов завершения
транзакции. Все эти
операторы можно
использовать как в модулях
SQL, так и во встроенном SQL.
Заметим, что в SQL/89 не
определен набор
операторов интерактивного
SQL (т.е. отсутствуют явные
спецификации набора
операторов SQL, которые
могут задаваться в
интерактивном режиме).
2.6.1 Операторы, связанные
с курсором
Операторы этой группы
объединяет то, что все они
работают с некоторым
курсором, объявление
которого должно
содержаться в том же
модуле или программе со
встроенным SQL. Если
говорить неформально,
курсор - это механизм языка
SQL, предназначенный для
того, чтобы позволить
прикладной программе
последовательно, строка за
строкой, просмотреть
результат связанного с
курсором запроса.
2.6.1.1 ОПЕРАТОР ОБЪЯВЛЕНИЯ
КУРСОРА
Для удобства мы повторим
здесь синтаксические
правила объявления
курсора, приведенные в
подразделе 2.3.1:
<declare cursor> ::=
DECLARE <cursor name> CURSOR
FOR <cursor specification>
<cursor specification> ::=
<query expression> [<order by clause>...]
<query expression> ::=
<query term>
| <query expression> UNION [ALL] <query term>
<query term> ::=
<query specification> | (<query expression>)
<order by clause> ::=
ORDER BY <sort specification>
[{,<sort specification>}...]
<sort specification> ::=
{ <unsigned integer> | <column specification> }
[ASC | DESC]
В объявлении курсора
могут задаваться запросы
наиболее общего вида с
возможностью выполнения
операции UNION и сортировки
конечного результата. Этот
оператор не является
выполняемым, он только
связывает имя курсора со
спецификацией курсора.
2.6.1.2 ОПЕРАТОР ОТКРЫТИЯ
КУРСОРА
Оператор описывается
следующим синтаксическим
правилом:
<open statement> ::=
OPEN <cursor name>
В реализациях
встроенного SQL обычно
требуется, чтобы
объявление курсора
текстуально
предшествовало оператору
открытия курсора. Оператор
открытия курсора должен
быть первым в серии
выполняемых операторов,
связанных с заданным
курсором. При выполнении
этого оператора
производится подготовка
курсора к работе над ним. В
частности, в этот момент
производится связывание
спецификации курсора со
значениями переменных
основного языка в случае
встроенного SQL или
параметров в случае модуля
(это значит, что после
выполнения оператора
открытия курсора любые
изменения переменных
основной программы не
будут оказывать какие-либо
действия на результат
запроса, связанного с
курсором).
В большинстве реализаций
в случае встроенного SQL
именно выполнение
оператора открытия
курсора приводит к
компиляции спецификации
курсора и подготовке
выполняемого плана
запроса. Можно считать
(хотя фактически это
делается далеко не всегда),
что во время выполнения
оператора открытия
курсора производится
построение временной
таблицы, содержащей
результат запроса, который
связан с этим курсором.
Следующие операторы
можно выполнять над
открытым курсором в
произвольном порядке.
2.6.1.3 ОПЕРАТОР ЧТЕНИЯ
ОЧЕРЕДНОЙ СТРОКИ КУРСОРА
Синтаксис оператора
чтения следующий:
<fetch statement> ::=
FETCH <cursor name> INTO <fetch target list>
<fetch target list> ::=
<target specification>[{,<target specification>}...]
В операторе чтения
указывается имя курсора и
обязательный раздел INTO,
содержащий список
спецификаций назначения
(список имен переменных
основной программы в
случае встроенного SQL или
имен "выходных"
параметров в случае модуля
SQL). Число и типы данных в
списке назначений должны
совпадать с числом и
типами данных списка
выборки спецификации
курсора.
Любой открытый курсор
всегда имеет позицию: он
может быть установлен
перед некоторой строкой
результирующей таблицы
(перед первой строкой
сразу после открытия
курсора), на некоторую
строку результата или за
последней строкой
результата.
Если таблица, на которую
указывает курсор, является
пустой, или курсор
позиционирован на
последнюю строку или за
ней, то при выполнении
оператора чтения курсор
устанавливается в позицию
после последней строки,
параметру SQLCODE
присваивается значение 100,
никакие значения не
присваиваются целям,
указанным в разделе INTO.
Если курсор установлен в
позицию перед строкой, то
при выполнении оператора
чтения он устанавливается
на эту строку, и значения
строки присваиваются
соответствующим целям.
Если курсор установлен
на строку r, отличную от
последней строки, то
курсор устанавливается на
строку, непосредственно
следующую за строкой r (в
порядке, определенном
реализацией, если запрос
не содержит раздела ORDER BY),
и значения из этой
следующей строки
присваиваются
соответствующим целям.
Возникает естественный
вопрос, каким образом
можно параметризовать
курсор неопределенным
значением или узнать, что
выбранное из очередной
строки значение является
неопределенным. Это
достигается в SQL/89 за счет
использования так
называемых индикаторных
параметров и переменных.
Если известно, что
значение, передаваемое из
основной программы СУБД
или принимаемое основной
программой от СУБД, может
быть неопределенным, и
этот факт интересует
прикладного программиста,
то спецификация параметра
в операторе SQL имеет вид:
<parameter name>[INDICATOR]<parameter
name>, а спецификация
переменной -<embedded variable
name> [INDICATOR] <embedded variable
name>. Отрицательное
значение индикаторного
параметра или
индикаторной переменной
(они должны быть целого
типа) соответствует
неопределенному значению
параметра или переменной.
2.6.1.4 ОПЕРАТОР
ПОЗИЦИОННОГО УДАЛЕНИЯ
Синтаксис этого
оператора следующий:
<delete statement: positioned> ::=
DELETE FROM <table name>
WHERE CURRENT OF <cursor name>
Если указанный в
операторе курсор открыт и
установлен на некоторую
строку, и курсор
определяет изменяемую
таблицу, то текущая строка
курсора удаляется, а он
позиционируется перед
следующей строкой.
Таблица, указанная в
разделе FROM оператора DELETE,
должна быть таблицей,
указанной в самом внешнем
разделе FROM спецификации
курсора.
2.6.1.5 ОПЕРАТОР
ПОЗИЦИОННОЙ МОДИФИКАЦИИ
Оператор описывается
следующими
синтаксическими
правилами:
<update statement: positioned> ::=
UPDATE <table name>
SET <set clause:positioned>
[{,<set clause:positioned>}...]
WHERE CURRENT OF <cursor name>
<set clause: positioned> ::=
<object column:positioned> =
{ <value expression> | NULL }
<object column: positioned> ::= <column name>
Если указанный в
операторе курсор открыт и
установлен на некоторую
строку и курсор определяет
изменяемую таблицу, то
текущая строка курсора
модифицируется в
соответствии с разделом SET.
Позиция курсора не
изменяется. Таблица,
указанная в разделе FROM
оператора DELETE, должна быть
таблицей, указанной в
самом внешнем разделе FROM
спецификации курсора.
Замечание: требование
указывать имя таблицы в
операторах позиционного
удаления и позиционной
модификации является,
очевидно, избыточным,
поскольку до имени таблицы
можно добраться через имя
курсора. Единственной
возможной причиной этой
избыточности может быть
упрощение реализации (хотя
не очень понятно, что
именно упрощается).
2.6.1.6 ОПЕРАТОР ЗАКРЫТИЯ
КУРСОРА
Синтаксис этого
оператора следующий:
<close statement>::=
CLOSE <cursor name>
Если к моменту
выполнения этого
оператора курсор
находился в открытом
состоянии, то оператор
переводит курсор в
закрытое состояние. После
этого над курсором
возможно выполнение
только оператора OPEN.
2.6.2 Одиночные операторы
манипулирования данными
Каждый из операторов
этой группы является
абсолютно независимым от
какого бы то ни было
другого оператора, т.е. для
выполнения любого
оператора этой группы не
требуется предварительное
выполнение какого бы то ни
было другого оператора.
2.6.2.1 ОПЕРАТОР ВЫБОРКИ
Для удобства читателей
мы повторяем синтаксис
этого оператора еще раз:
<select statement> ::=
SELECT [ALL | DISTINCT] <select name>
INTO <select target list>
<table expression>
<select target list>::=
<target specification> [{,<target specification>}...]
Поскольку, как мы уже
объясняли в подразделе 2.3.2,
результатом одиночного
оператора выборки должна
являться таблица,
состоящая не более, чем из
одной строки, список целей
специфицируется в самом
операторе. После
выполнения оператора цели
содержат соответствующие
поля (столбцы)
результирующей строки
(если, конечно,
результирующая таблица не
является пустой).
2.6.2.2 ОПЕРАТОР ПОИСКОВОГО
УДАЛЕНИЯ
Оператор описывается
следующим синтаксическим
правилом:
<delete statement: searched> ::=
DELETE FROM <table name>
WHERE [<search condition>]
Таблица T, указанная в
разделе FROM оператора DELETE,
должна быть изменяемой. На
условие поиска
накладывается то условие,
что на столбцы таблицы T не
должны содержаться ссылки
ни в каком вложенном
подзапросе предикатов
раздела WHERE.
Фактически, оператор
выполняется следующим
образом: последовательно
просматриваются все
строки таблицы T, и те
строки, для которых
результатом вычисления
условия выборки является
true, удаляются из таблицы T.
При отсутствии раздела WHERE
удаляются все строки
таблицы T (обычно при
выполнении поискового
оператора DELETE без раздела
WHERE в интерактивном режиме
до удаления всех строк
запрашивается
подтверждение
правильности такого
действия).
2.6.2.3 ОПЕРАТОР ПОИСКОВОЙ
МОДИФИКАЦИИ
Оператор обладает
следующим синтаксисом:
<update statement: searched> ::=
UPDATE <table name>
SET <set clause: searched>
[{,<set clause: searched>}...]
[WHERE <search conditions>]
<set clause: searched> ::=
<object column: searched> =
{ <value expression> | NULL }
<object column: searched> ::= <column name>
Таблица T, указанная в
операторе UPDATE, должна быть
изменяемой. На условие
поиска накладывается то
условие, что на столбцы
таблицы T не должны
содержаться ссылки ни в
каком вложенном
подзапросе предикатов
раздела WHERE.
Оператор фактически
выполняется следующим
образом: таблица T
последовательно
просматривается, и каждая
строка, для которой
результатом вычисления
условия поиска является
true, изменяется в
соответствии с разделом SET.
Если арифметическое
выражение в разделе SET
содержит ссылки на столбцы
таблицы T, то при
вычислении
арифметического выражения
используются значения
столбцов текущей строки до
их модификации.
2.6.3 Операторы окончания
транзакции
Текущая транзакция может
быть завершена успешно (с
фиксацией в базе данных
произведенных изменений)
путем выполнения
оператора COMMIT WORK или
аварийно (с удалением из
базы данных изменений,
произведенных текущей
транзакцией) путем
выполнения оператора ROLLBACK
WORK. При выполнении любого
из этих операторов
производится
принудительное закрытие
всех курсоров, открытых к
моменту выполнения
оператора завершения
транзакции.
3.
Динамический SQL в Oracle V.6
Описанный в стандарте
SQL/89 набор операторов SQL
предназначен для
встраивания в программу на
обычном языке
программирования. Поэтому
в этом наборе перемешаны
операторы "истинного"
реляционного языка
запросов (например
оператор удаления из
таблицы части строк,
удовлетворяющих заданному
условию) и операторы
работы с курсорами,
позволяющими обеспечить
построчный доступ к
таблице-результату
запроса.
Понятно, что в диалоговом
режиме набор операторов SQL
и их синтаксис должен быть
несколько другим. Весь
вопрос состоит в том, как
реализовывать такую
диалоговую программу.
Правила встраивания
стандартного SQL в
программу на обычном языке
программирования
предусматривают, что вся
информация, касающаяся
операторов SQL, известна в
статике (за исключением
значений переменных,
используемых в качестве
констант в операторах SQL).
Не предусмотрены
стандартные средства
компиляции с последующим
выполнением операторов,
которые становятся
известными только во время
выполнения (например
вводятся с терминала).
Поэтому, опираясь только
на стандарт, невозможно
реализовать диалоговый
монитор взаимодействия с
БД на языке SQL или другую
прикладную программу, в
которой текст операторов
SQL возникает во время
выполнения, т.е. фактически
так или иначе стандарт
необходимо расширять.
Один из возможных путей
расширения состоит в
использовании специальной
группы операторов,
обеспечивающих
динамическую компиляцию
(во время выполнения
прикладной программы)
базового подмножества
операторов SQL и
поддерживающих их
корректное выполнение.
Некоторый набор таких
операторов входил в
диалект SQL, реализованный в
System R, несколько отличный
набор входит в реализацию
Oracle V.6, и, наконец, в новом
стандарте SQL/92 появилась
стандартная версия
динамического SQL.
Поскольку в СУБД Oracle
средства динамического SQL
реализованы уже
сравнительно давно, имеет
смысл рассмотреть сначала
их, чтобы иметь основу для
сравнения с SQL/92.
Замечание: мы говорим
здесь именно об Oracle V.6, а не
о последней, седьмой
версии, поскольку в Oracle V.7
имеется реализация
динамического SQL,
соответствующая стандарту
SQL/92.
В дополнительный набор
операторов,
поддерживающих
динамическую компиляцию
базовых операторов SQL,
входят операторы: PREPARE,
DESCRIBE и EXECUTE.
3.1 Оператор
подготовки
Оператор PREPARE имеет
синтаксис:
<prepare-statement> ::=
PREPARE <statement-name> FROM <host-string-variable>
<statement-name> ::= <name>
Во время выполнения
оператора PREPARE символьная
строка, содержащаяся в
host-string-variable, передается
компилятору SQL, который
обрабатывает ее почти
таким же образом, как если
бы получил в статике.
Построенный при
выполнении оператора PREPARE
код остается действующим
до конца транзакции или до
повторного выполнения
данного оператора PREPARE в
пределах этой же
транзакции.
В отличие от операторов
SQL, статически
подставляемых в программу
на обычном языке
программирования, в
которых связь с
переменными включающей
программы производится по
именам (т.е. в соответствии
со стандартом во
встроенном операторе SQL
могут употребляться
просто имена переменных
включающей программы),
динамическая природа
операторов,
подготавливаемых с
помощью оператора PREPARE,
заставляет рассматривать
эти имена как имена
формальных параметров.
Соответствие этих
формальных параметров
адресам переменных
включающей программы
устанавливается
позиционно во время
выполнения
подготовленного
оператора.
3.2 Оператор
получения описания
подготовленного оператора
Оператор DESCRIBE
предназначен для того,
чтобы определить тип ранее
подготовленного
оператора, узнать
количество и типы
формальных параметров
(если они есть) и
количество и типы столбцов
результирующей таблицы,
если подготовленный
оператор является
оператором выборки (SELECT).
Мы не приводим синтаксис
оператора DESCRIBE, поскольку
этот синтаксис мало что
проясняет.
Действие оператора DESCRIBE
состоит в том, что в
указанную область памяти
прикладной программы
(структура этой области
фиксирована и известна
пользователям) помещается
информация,
характеризующая ранее
подготовленный оператор с
заданным именем.
3.3 Оператор
выполнения
подготовленного оператора
Оператор EXECUTE служит для
выполнения ранее
подготовленного оператора
SQL типа "N" (не
требующего применения
курсора) или для
совмещенной подготовки и
выполнения такого
оператора. Синтаксис
оператора EXECUTE:
<execute-statement> ::=
EXECUTE
{ <statement-name> [USING <host-vars-list>]
| IMMEDIATE <host-string-variable> }
Для выполнения
подготовленного оператора
служит первый вариант
оператора EXECUTE. В этом
случае <statement-name> должен
задавать имя,
употреблявшееся ранее в
операторе PREPARE. Если в
подготовленном операторе
присутствуют формальные
параметры, то в операторе
EXECUTE должен задаваться
список фактических
параметров <host-vars-list>.
Число и типы фактических
параметров должны
соответствовать числу и
типам формальных
параметров
подготовленного
оператора.
Второй вариант оператора
EXECUTE предназначен для
совмещенной подготовки и
выполнения оператора SQL
типа "N". В этом случае
параметром оператора EXECUTE
является строка, которая
должна содержать текст
оператора SQL (эту строку
разрешается также
задавать литерально).
Запрещается использование
в этом операторе
переменных включающей
программы (формальных
параметров).
3.4 Работа с
динамическими операторами
SQL через курсоры
Для использования таких
операторов используется
расширение механизма
курсоров стандарта SQL.
Во-первых, при определении
курсора можно указывать не
только литеральную
спецификацию курсора, но и
имя оператора, вводимое с
помощью оператора PREPARE (в
этом случае оператор PREPARE
должен текстуально
находиться выше оператора
DECLARE). Тем самым полный
синтаксис оператора DECLARE
становится следующим:
<declare cursor> ::=
DECLARE <cursor name> CURSOR
FOR { <cursor specification> | <statement-name> }
Далее, поскольку для
такого курсора в статике
неизвестна информация о
входных и выходных
переменных включающей
программы, то используется
другая форма операторов OPEN
и FETCH.
Полный синтаксис этих
операторов становится
следующим:
<open statement> ::=
OPEN <cursor name>
[USING { <host-vars-list> | DESCRIPTOR <descr-name> }]
<fetch statement> ::=
FETCH <cursor name>
{ INTO <fetch target list> |
USING <host-vars-list> |
USING DESCRIPTOR <descr-name> }
Как видно, предлагается
два способа задания
фактических входных и
выходных параметров:
прямое с указанием в
операторах OPEN и/или FETCH
списков имен переменных
включающей программы и
косвенное, когда число
параметров и их адреса
сообщаются через
дополнительную
структуру-дескриптор.
Первый способ
предлагается использовать
для работы с операторами
выборки, для которых
фиксирован набор
формальных входных и
выходных параметров.
Точнее говоря, что
касается выходных
параметров, должны быть
фиксированы число и типы
элементов списка выборки.
Второй способ работы с
динамически
откомпилированными
операторами, требующими
использования курсоров,
состоит в использовании
дескрипторов динамически
формируемых списков
параметров. В этом случае
вся ответственность за
соответствие типов
фактических и формальных
параметров ложится на
программиста. В результате
ошибки при формировании
такого списка, в частности,
может быть испорчена
память Си-программы.
4. Некоторые
черты SQL/92
Огромный объем стандарта
SQL/92 и ограниченный объем
этой статьи не позволяют
нам описать этот стандарт
сколько-нибудь подробно.
Кроме того, как отмечалось
выше, на сегодняшний день
все еще отсутствует какая
бы то ни было полная
реализация SQL/92. Тем не
менее мы считаем полезным
сравнительно подробно
описать стандартные
средства динамического SQL
(это описание можно
использовать хотя бы в
качестве эталона при
сравнении различных
реализаций) и привести
сводку основных отличий
SQL/92 от SQL/89 (в этом мы будем
следовать последнему
изданию книги Дейта
"Стандарт SQL").
4.1
Динамический SQL в
стандарте SQL/92
Набор операторов
динамического SQL в
стандарте SQL/92 существенно
шире того, который был
реализован в Oracle V.6. В
основном это связано с тем,
что введены операторы для
работы с дескрипторами, а
также появились
подготавливаемые
операторы позиционного
удаления и позиционной
модификации.
4.1.1 Оператор выделения
памяти под дескриптор
Оператор имеет следующий
синтаксис:
<allocate descriptor statement> ::=
ALLOCATE DESCRIPTOR <descriptor name>
[WITH MAX <occurrences>]
<occurences> ::= <simple value specification>
<descriptor name> ::=
[<scope option>] <simple value specification>
<scope option> ::= GLOBAL | LOCAL
<simple value specification> ::=
<parameter name>
| <embedded variable name>
| <literal>
Дескриптор - это
динамически выделяемая
часть памяти прикладной
программы, служащая для
принятия информации о
результате или параметрах
динамически
подготовленного оператора
SQL или задания параметров
такого оператора. Смысл
того, что для выделения
памяти используется
оператор SQL, а не просто
стандартная функция alloc
или какая-нибудь другая
функция динамического
запроса памяти, состоит в
том, что прикладная
программа может теперь не
знать структуру
дескриптора и даже его
адрес. Это позволяет не
привязывать SQL к
особенностям какой-либо
системы программирования
или ОС. Все обмены
информацией между
собственно прикладной
программой и
дескрипторами
производятся также с
помощью специальных
операторов SQL (GET и SET, см.
ниже).
Далее возникает вопрос:
зачем вообще выделять
память под дескрипторы
динамически? Это нужно
потому, что в общем случае
прикладная программа,
использующая динамический
SQL, не знает в статике число
одновременно действующих
динамических операторов
SQL, описание которых может
потребоваться. С этим же
связано то, что имя
дескриптора может
задаваться как
литеральной строкой
символов, так и через
строковую переменную
включающего языка, т.е. его
можно генерировать во
время выполнения
программы.
В операторе ALLOCATE DESCRIPTOR,
помимо прочего, может
указываться число
описательных элементов, на
которое он рассчитан. Если,
например, при выделении
памяти под дескриптор в
разделе WITH MAX указано целое
положительное число N, а
потом дескриптор
используется для описания
M (M>N) элементов (например M
столбцов результата
запроса), то это приводит к
возникновению
исключительной ситуации.
4.1.2 Оператор
освобождения памяти из-под
дескриптора
Синтаксис оператора:
<deallocate descriptor statement> ::=
DEALLOCATE DESCRIPTOR <descriptor name>
Выполнение этого
оператора приводит к
освобождению памяти из-под
ранее выделенного
дескриптора. После этого
использование имени
дескриптора незаконно в
любом операторе, кроме
ALLOCATE DESCRIPTOR.
4.1.3 Оператор получения
информации из области
дескриптора SQL
Оператор определяется
следующими
синтаксическими
правилами:
<get descriptor statement> ::=
GET DESCRIPTOR <descriptor name>
<get descriptor information>
<get descriptor information> ::=
<get count>
| VALUE <item number>
<get item information>
[{<comma> <get item information>}...]
<get count> ::=
<simple target specification 1>
<equals operator> COUNT
<get item information> ::=
<simple target specification 2>
<equals operator>
<descriptor item name>
<item number> ::= <simple value specification>
<simple target specification 1> ::=
<simple target specification>
<simple target specification 2> ::=
<simple target specification>
<descriptor item name> ::=
TYPE
| LENGHT
| OCTET_LENGHT
| RETURNED_LENGHT
| RETURNED_OCTET_LENGHT
| PRECISION
| SCALE
| DATETIME_INTERVAL_CODE
| DATATIME_INTERVAL_PRECISION
| NULLABLE
| INDICATOR
| DATA
| NAME
| UNNAMED
| COLLATION_CATALOG</PRE>
| COLLATION_SCHEMA
| COLLATION_NAME
| CHARACTER_SET_CATALOG
| CHARACTER_SET_SCHEMA
| CHARACTER_SET_NAME
<simple target specification> ::=
<parameter name>
| <embedded variable name>
Оператор GET DESCRIPTOR служит
для выборки описательной
информации, ранее
размещенной в дескрипторе
с помощью оператора DESCRIBE
(см. п. 4.1.7). За
одно выполнение оператора
можно получить либо число
заполненных элементов
дескриптора (COUNT), либо
информацию, содержащуюся в
одном из заполненных
элементов.
4.1.4 Оператор установки
дескриптора
Оператор установки имеет
следующий синтаксис:
<set descriptor statement> ::=
SET DESCRIPTOR <descriptor name>
<set descriptor information>
<set descriptor information> ::=
<set count>
| VALUE <item number>
<set item information>
[{<comma> <set item information>}...]
<set count> ::=
COUNT <equals operator>
<simple value specification 1>
<set item information> ::=
<descriptor item name>
<equals operator>
<simple value specification 2>
<simple target specification 1> ::=
<simple target specification>
<simple target specification 2> ::=
<simple target specification>
<item number> ::= <simple value specification>
Оператор SET DESCRIPTOR служит
для заполнения элементов
дескриптора с целью его
будущего использования в
разделе USING. За одно
выполнение оператора
можно поместить значение в
поле COUNT (число заполненных
элементов) либо частично
или полностью
сформировать один элемент
дескриптора.
4.1.5 Оператор подготовки
Оператор определяется
следующим синтаксисом:
<prepare statement> ::=
PREPARE <SQL statement name>
FROM <SQL statement variable>
<SQL statement variable> ::=
<simple target specification>
<preparable statement> ::=
<preparable SQL data statement>
| <preparable SQL schema statement>
| <preparable SQL transaction statement>
| <preparable SQL session statement>
| <preparable implementation-defined statement>
<preparable SQL data statement> ::=
<delete statement: searched>
| <dynamic single row select statement>
| <insert statement>
| <dynamic select statement>
| <update statement: searched>
| <preparable dynamic delete statement: positioned>
| <preparable dynamic update statement: positioned>
<preparable SQL schema statement> ::=
<SQL schema statement>
<preparable SQL transaction statement> ::=
<SQL transaction statement>
<preparable SQL session statement> ::=
<SQL session statement>
<dynamic select statement> ::=
<cursor specification>
<dynamic simple row select statement> ::=
<query specification>
<SQL statement name> ::=
<statement name>
| <extended statement name>
<extended statement name> ::=
[scope option] <simple value specification>
<cursor specification> ::=
<query expression> [<order by clause>]
[<updatability clause>]
<updatability clause> ::=
FOR { READ ONLY | UPDATE [ OF <column name list> ] }
<query expression> ::=
<non-join query expression>
| <joined table>
<query specification> ::=
SELECT [<set quantifier>]
<select list> <table expression>
<set quantifier> ::= DISTINCT | ALL
Оператор PREPARE вызывает
компиляцию и построение
плана выполнения
оператора SQL, заданного в
текстовой форме. После
успешного выполнения
оператора PREPARE с
подготовленным оператором
связывается указанное
(литерально или косвенно)
имя этого оператора,
которое потом может быть
использовано в операторах
DESCRIBE, EXECUTE, OPEN CURSOR, ALLOCATE CURSOR
и DEALLOCATE PREPARE. Эта связь
сохраняется до явного
выполнения оператора
DEALLOCATE PREPARE.
4.1.6 Оператор отказа от
подготовленного оператора
Синтаксис оператора
следующий:
<deallocate prepared statement> ::=
DEALLOCATE PREPARE <SQL statement name>
Выполнение этого
оператора приводит к тому,
что ранее подготовленный
оператор SQL, связанный с
указанным именем
оператора, ликвидируется,
и, соответственно, имя
оператора становится
неопределенным. Если
подготовленный оператор
являлся оператором
выборки, и к моменту
выполнения оператора
DEALLOCATE существовал
открытый курсор, связанный
с именем подготовленного
оператора, то оператор
DEALLOCATE возвращает код
ошибки. Если же для
подготовленного оператора
выборки существовал
неоткрытый курсор,
образованный с помощью
оператора ALLOCATE CURSOR, то этот
курсор ликвидируется. Если
курсор объявлялся
оператором DECLARE CURSOR, то
такой курсор переходит в
состояние, существовавшее
до выполнения оператора
PREPARE. Если с курсором был
связан подготовленный
оператор (динамический DELETE
или UPDATE), то для этих
операторов выполняется
неявный оператор DEALLOCATE.
4.1.7
Оператор запроса описания
подготовленного оператора
Оператор определяется
следующим синтаксисом:
<describe statement> ::=
<describe input statement>
| <describe output statement>
<describe input statement> ::=
DESCRIBE INPUT <SQL statement name>
<using descriptor>
<describe output statement> ::=
DESCRIBE [OUTPUT] <SQL statement name>
<using descriptor>
<using clause> ::=
<using arguments>
| <using descriptor>
<using arguments> ::=
{ USING | INTO }
<argument> [{<comma> <argument>}...]
<argument> ::= <target specification>
<using descriptor> ::=
{ USING | INTO }
SQL DESCRIPTOR <descriptor name>
<target specification> ::=
<parameter specification>
| <variable specification>
<parameter specification> ::=
<parameter name> [<indicator parameter>]
<indicator parameter> ::=
[INDICATOR] <parameter name>
<variable specification> ::=
<embedded variable name> [<indicator variable>]
<indicator variable> ::=
[INDICATOR] <embedded variable name>
При выполнении оператора
DESCRIBE происходит
заполнение указанного в
операторе дескриптора
информацией, описывающей
либо результат ранее
подготовленного оператора
SQL (если это оператор
выборки), либо количество и
типы параметров
подготовленного
оператора. В <using descriptor>
полагается писать USING SQL
DESCRIPTOR.
4.1.8 Оператор выполнения
подготовленного оператора
Синтаксис оператора
следующий:
<execute statement> ::=
EXECUTE <SQL statement name>
[<result using clause>]
[<parameter using clause>]
<result using clause> ::= <using clause>
<parameter using clause> ::= <using clause>
Оператор EXECUTE может быть
применен к любому ранее
подготовленному оператору
SQL, кроме <dynamic select statement>.
Если это оператор <dynamic
single row select statement>, то
оператор EXECUTE должен
содержать раздел <result using
class> с ключевым словом INTO.
В любом случае число
фактических параметров,
задаваемых через разделы
using, должно
соответствовать числу
формальных параметров,
определенных в
подготовленном операторе
SQL.
4.1.9 Оператор подготовки
с немедленным выполнением
Синтаксис оператора:
<execute immediate statement> ::=
EXECUTE IMMEDIATE <SQL statement variable>
При выполнении оператора
EXECUTE IMMEDIATE производится
подготовка и немедленное
выполнение заданного в
текстовой форме оператора
SQL. При этом
подготавливаемый оператор
не должен быть оператором
выборки, не должен
содержать формальных
параметров и комментариев.
4.1.10 Оператор объявления
курсора над динамически
подготовленным оператором
выборки
Оператор определяется
следующим синтаксисом:
<dynamic declare cursor> ::=
DECLARE <cursor name> [INSENSITIVE] [SCROLL]
CURSOR FOR <statement name>
Как определяется в новом
стандарте, для всех
операторов DECLARE CURSOR
курсоры фактически
создаются при начале
транзакции и уничтожаются
при ее завершении. Заметим,
что в этом операторе <cursor
name> и <statement name> прямо
(литерально) заданные
идентификаторы.
4.1.11 Другая
разновидность оператора
определения курсора над
динамически
подготовленным оператором
выборки
Для этого оператора
действуют следующие
синтаксические правила:
<allocate cursor statement> ::=
ALLOCATE <extended cursor name> [INSENSITIVE] [SCROLL]
CURSOR FOR <extended statement name>
<extended cursor name> ::=
[<scope option>] <simple value specification>
Курсоры, определяемые с
помощью оператора ALLOCATE
CURSOR, фактически создаются
при выполнении такого
оператора и уничтожаются
при выполнении оператора
DEALLOCATE PREPARE или при конце
транзакции. В этом
операторе имена курсора и
подготовленного оператора
SQL могут задаваться не
только в литеральной
форме, но и через
переменные (т.е. может
использоваться косвенное
именование).
<scope option> относится к
области видимости имен: в
пределах текущего модуля
или в пределах текущей
сессии.
4.1.12 Оператор открытия
курсора, связанного с
динамически
подготовленным оператором
выборки
Синтаксис оператора
открытия курсора
следующий:
<dynamic open statement> ::=
OPEN <dynamic cursor name> [<using clause>]
По сути оператор
открытия курсора,
связанного с динамически
подготовленным оператором
SQL, отличается от
статического случая
только возможным наличием
раздела using, в котором
задаются фактические
параметры оператора
выборки. Кроме того, имя
курсора может задаваться
через переменную (т.е.
косвенным образом).
4.1.13 Оператор чтения
строки по курсору,
связанному с динамически
подготовленным оператором
выборки
Синтаксис:
<dynamic fetch statement> ::=
FETCH [[<fetch orientation>] FROM]
<dynamic cursor name> <using clause>
На самом деле оператор
чтения по курсору,
связанному с динамически
подготовленным оператором
SQL, отличается от
статического случая
только возможным наличием
раздела using, в котором
задается размещение
значений текущей строки
результирующей таблицы.
Кроме того, имя курсора
может задаваться через
переменную.
4.1.14 Оператор закрытия
курсора, связанного с
динамически
подготовленным оператором
выборки
Оператор закрытия
курсора определяется
следующим синтаксическим
правилом:
<dynamic close statement> ::=
CLOSE <dynamic cursor name>
Оператор закрытия
курсора, связанного с
динамически
подготовленным оператором
SQL, отличается от
статического случая
только тем, что имя курсора
может задаваться через
переменную.
4.1.15 Оператор
позиционного удаления по
курсору, связанному с
динамически
подготовленным оператором
выборки
Синтаксис:
<dynamic delete statement: positioned> ::=
DELETE FROM <table name>
WHERE CURRENT OF <dynamic cursor name>
Оператор позиционного
удаления по курсору,
связанному с динамически
подготовленным оператором
SQL, отличается от
статического случая
только тем, что имя курсора
может задаваться через
переменную.
4.1.16 Оператор
позиционной модификации
по курсору, связанному с
динамически
подготовленным оператором
выборки
Оператор определяется
следующим синтаксическим
правилом:
<dynamic update statement: positioned> ::=
UPDATE <table name>
SET <set clause> [{<comma> <set clause>}...]
WHERE CURRENT OF <dynamic cursor name>
Оператор позиционной
модификации по курсору,
связанному с динамически
подготовленным оператором
SQL, отличается от
статического случая
только тем, что имя курсора
может задаваться через
переменную.
4.1.17
Подготавливаемый оператор
позиционного удаления
Синтаксис оператора:
<preparable dynamic delete statement: positioned> ::=
DELETE [FROM <table name>]
WHERE CURRENT OF <cursor name>
Основной резон появления
этого и следующего
операторов состоит в том,
что сочетание курсора,
определенного на
динамически
подготовленном операторе
выборки, и статически
задаваемых операторах
удаления и модификации по
этому курсору, выглядит
довольно нелепо. Поэтому в
стандарте появились
динамически
подготавливаемые
позиционные операторы
удаления и вставки.
Естественно, что
выполняться они должны с
помощью оператора EXECUTE.
4.1.18 Подготавливаемый
оператор позиционной
модификации
<preparable dynamic update statement: positioned> ::=
UPDATE [<table name>]
SET <set clause> [{<comma> <set clause>}...]
WHERE CURRENT OF <cursor name>
См. п. 4.1.17.
Если внимательно
сравнить средства
динамического SQL СУБД Oracle
V.6 и стандарта SQL/92, то
видно, что Oracle практически
вкладывается в стандарт,
если не считать небольших
синтаксических различий и
(что существенно более
важно) разного стиля
работы с дескрипторами.
Думается, что примерно
такая же ситуация имеет
место в других СУБД,
поддерживающих
динамический SQL.
Поэтому нашими
рекомендациями при
использовании
динамического SQL в
прикладных программах
являеются следующие (если,
конечно, вы не хотите
дождаться повсеместной и
полной реализации SQL/92):
4.2 Сводка
отличий SQL/92 от SQL/89
В этом разделе
содержится краткая сводка
различий между SQL/92 и SQL/89.
Синтаксические и
семантические детали
конструкций SQL/92 не
приводятся. Еще раз
подчеркнем, что в
изложении мы следуем книге
Дейта "Стандарт SQL".
4.2.1 Расширения языка
В языке, определяемом
стандартом SQL/92, содержится
много свойств, которые
отсутствовали в языке SQL/89.
Ниже приводится краткая
сводка этих свойств.
4.2.1.1 ТИПЫ ДАННЫХ
Появилась возможность
использования типа данных
символьных строк
переменной длины (т.е. при
спецификации столбца
указывается предельно
допустимый размер
хранимой строки в
символах, а реально в базе
данных хранится ровно
столько символов, сколько
их ввел пользователь).
Введены типы данных
битовых строк постоянной и
переменной длины (как они
реально хранятся в базе
данных, в стандарте не
определяется). Наконец,
стандартизованы
темпоральные типы данных
DATE (дата), TIME (время) и INTERVAL
(временной интервал).
4.2.1.2
ИНТЕРНАЦИОНАЛИЗАЦИЯ И
НАЦИОНАЛИЗАЦИЯ
При определении схемы
базы данных или
впоследствии можно
определить особенности
национального набора
символов, включая правила
упорядочения. Могут
определяться наборы
символов, используемые как
в хранимых текстовых
строках, так и в
идентификаторах.
4.2.1.3 ОПРЕДЕЛЕНИЕ СХЕМЫ
БД И МАНИПУЛИРОВАНИЕ
СХЕМОЙ БД
Наконец-то появилась
возможность создавать
хранимые и представляемые
таблицы и задавать или
удалять привилегии
доступа (операторы CREATE TABLE,
CREATE VIEW, GRANT, REVOKE) в любой
момент времени в любой
транзакции вне оператора
определения схемы.
Появились операторы
уничтожения таблиц (DROP TABLE
и DROP VIEW), которые также
можно выполнять внутри
любой транзакции (при
наличии соответствующих
привилегий). Вообще,
следует заметить, что в
стандарте SQL/92 для любого
оператора класса CREATE
существует парный
оператор класса DROP.
Специфицирован также
оператор ALTER TABLE,
позволяющий динамически
изменять характеристики
ранее созданной таблицы (в
частности добавлять к ней
новые столбцы). Все
упомянутые здесь
операторы могут
включаться в модуль SQL.
4.2.1.4 ОГРАНИЧЕНИЯ
ЦЕЛОСНОСТИ
В добавление к
возможностям SQL/89
определения ограничений
целостности на уровне
столбца и/или таблицы в SQL/92
допустимо отдельное
определение ограничений,
распространяющееся в
общем случае на несколько
таблиц.
Появилась возможность
определения отложенных
(проверяемых при
завершении транзакции)
ограничений целостности.
Расширены возможности
определения ограничений
внешнего ключа
(ограничений ссылочной
целостности).
Введены средства
определения (CREATE DOMAIN),
изменения (ALTER DOMAIN) и отмены
определения (DROP DOMAIN)
домена. (На всякий случай
напомним читателям, что
домены имеют
непосредственную связь с
ограничениями
целостности, поскольку
домен определяет
потенциально возможное
множество значений
некоторого типа данных, а
при определении столбца
таблицы можно указать, к
какому домену будут
относиться значения этого
столбца. Тем самым другие
значения допускаться не
должны.)
4.2.1.5 ПРЕДСТАВЛЕНИЯ
В стандарте SQL/92
осмысленно ослаблены
требования к изменяемым
представлениям (в условии
выборки допускаются
подзапросы, не
коррелирующие со
столбцами таблицы разделы
FROM основного запроса).
Заметим, что множество
изменяемых запросов SQL/92
по-прежнему не включает
все представления, которые
теоречески являются
изменяемыми.
Уточнен смысл
конструкции WITH CHECK OPTION:
введены ключевые слова LOCAL
и CASCADE. При указании LOCAL
контролируется, что
измененная строка
останется видимой в том
представлении, для
которого выполнялся
оператор UPDATE. Если же
указывается CASCADE, то
изменение должно остаться
видимым в данном
представлении и во всех
представлениях, которые
определены над исходным
представлением (на самом
деле мы несколько упрощаем
ситуацию, для полного
анализа которой требуется
длительное рассмотрение
комбинаций наличия и
отсутствия конструкции WITH
CHECK OPTION у исходного
представления и того,
которое над ним
определено).
4.2.1.6 ТАБЛИЧНЫЕ ВЫРАЖЕНИЯ
Появились возможности
именования столбцов
результирующей таблицы и
самой таблицы. Именованные
табличные выражения можно
использовать, в частности,
в разделе FROM запросов.
(Раньше всегда было
непонятно, почему
табличное выражение,
результатом которого по
определению является
таблица, нельзя
использовать в качестве
элемента списка раздела
FROM.)
Появился новый класс
табличных выражений,
называемых "табличными
выражениями с
соединениями"
(join-table-expression), которые
можно использовать только
в разделе FROM. Такие
табличные выражения
строятся на основе базовых
и/или представляемых
таблиц на основе
использования разных
видов операции соединения:
CROSS JOIN (Декартово
произведение), INNER (обычное
соединение), LEFT и LEFT OUTER
(левое и левое внешнее
соединение), RIGHT и RIGHT OUTER
(правое и правое внешнее
соединение), FULL и FULL JOIN
(полное и полное внешнее
соединение) и UNION
(объединение). (Не уверен,
что от появления этого
класса табличных
выражений потенциальным
пользователям реализаций
SQL/92 станет жить легче, хотя
возможно станет легче
формулировать запросы
людям, привыкшим к
алгебраическому стилю
работы с базами данных.)
4.2.1.7 ВЫРАЖЕНИЯ ЗАПРОСОВ
При построении выражений
запросов (формально,
согласно синтаксису SQL/92,
соответствующие
конструкции не называются
выражениями запросов; тем
не менее мы предпочитаем
сохранить этот термин для
сближения с семантикой
SQL/89), кроме операции
теоретико-множественного
объединения UNION, которая
присутствовала в SQL/89,
стало возможным
использовать операции EXCEPT
(теоретико-множественное
вычитание) и INTERSECT
(теоретико-множественное
пересечение). Заметим для
точности, что возможность
получения в качестве
результата запроса
мультимножества строк (т.е.
с дубликатами) не
позволяет однозначно
интерпретировать сразу
все эти операции. Поэтому
результат одного и того же
выражения запросов в
разных реализациях может
быть разным.
4.2.1.8 КУРСОРЫ
При определении курсора
можно указывать ключевые
слова SCROLL и INSENSITIVE.
Указание SCROLL означает, что
курсор можно явно
позиционировать: на первую
строку результирующего
множества запроса, на
последнюю строку, на
строку с позицией с
положительным или
отрицательным смещением
от текущей строки, на
строку с явно указанным
абсолютным номером
позиции. Наличие ключевого
слова INSENSITIVE означает, что
какие бы изменения в
базовых таблицах не
производились в той же
транзакции, в которой
определен курсор, они не
должны влиять на
результирующее множество
строк курсора после его
открытия. Заметим, что,
хотя внешне эти
возможности выглядят
очень привлекательно, их
реализация стоит недешево.
И в том и в другом случае
требуется явное и почти
всегда полное построение
результирующего множества
запроса, связанного с
курсором.
4.2.1.9 УПРАВЛЕНИЕ
ТРАНЗАКЦИЯМИ И УРОВНИ
ИЗОЛЯЦИИ
Известно, что в
большинстве
SQL-ориентированных
реляционных СУБД
поддерживаются несколько
режимов изолированности
транзакций. В стандарте
SQL/92 специфицирован
оператор SET TRANSACTION, который,
в частности, позволяет
явно установить один из
следующих режимов,
влияющих на уровень
изолированности
транзакции: READ UNCOMMITTED, READ
COMMITTED, REPEATABLE READ, SERIALIZABLE.
В соответствии со
стандартом режим READ UNCOMMITTED
допускает наличие чтения
"грязных данных" (если
транзакция T1 работает в
этом режиме, то она может
прочитать данные,
обновленные транзакцией T2,
которая заканчивается
откатом; эти данные
"грязные", поскольку
никогда не будут
существовать в БД).
При установке режима READ
COMMITTED транзакция не сможет
прочитать "грязные
данные", но в ней может
возникнуть ситуация
"неповторяющегося
чтения" (пусть
транзакция T1 работает в
этом режиме и в ней
выполняется выборка
некоторой строки
некоторой таблицы; после
этого в транзакции T2
срабатывает оператор,
обновляющий эту строку;
теперь в транзакции T1
снова выполняется
оператор, выбирающий ту же
строку, и прикладная
программа или
интерактивный
пользователь с удивлением
обнаруживают, что значения
полей строки изменились).
Если устанавливается
режим REPEATABLE READ,
"неповторяющиеся
чтения" должны
гарантированно
отсутствовать, но возможно
возникновение
"строк-фантомов"
(пусть транзакция T1
работает в этом режиме и
выбирает некоторое
множество строк некоторой
таблицы в соответствии с
заданным условием; после
этого транзакция T2 заносит
в ту же таблицу новую
строку, удовлетворяющую
условию выборки
транзакции T1; теперь в
транзакции T1 повторно
срабатывает тот же самый
оператор выборки, и
прикладная программа или
интерактивный
пользователь с удивлением
обнаруживают, что
множество выбранных строк
отличается от того, каким
оно было при первом
выполнении оператора
выборки).
При установке режима
SERIALIZABLE должно
гарантироваться
отсутствие всех
перечисленных выше
эффектов. В этом случае
транзакция должна
выполняться так, как если
бы она выполнялась в
отсутствии всех
конкурирующих транзакций
(другими словами, смесь
транзакций, для которых
требуется полная
сериализация, должна
обрабатываться системой
так, чтобы суммарный
эффект был эквивалентен
некоторому
последовательному
выполнению этих
транзакций). В
соответствии со
стандартом режим SERIALIZABLE
должен являться режимом,
устанавливаемым для
транзакции по умолчанию
(если в ней не встречается
какой-либо оператор SET
TRANSACTION).
Кроме указания режима
изоляции в операторе SET
TRANSACTION можно указать,
является ли транзакция
только читающей базу
данных (READ ONLY) или
обновляющей (READ WRITE). По
умолчанию любая
транзакция считается
обновляющей, если только
не задан режим изоляции READ
UNCOMMITTED. В последнем случае
транзакция полагается
только читающей. Другими
словами, комбинация READ WRITE
и READ UNCOMMITTED является
недопустимой.
4.2.1.10 ДИНАМИЧЕСКИЙ SQL
В стандарте определены
операторы динамического
SQL. См. разд. 4.1.
4.2.1.11 ВСТРОЕННЫЙ SQL
Как отмечалось в разд. 2.5,
стандарт SQL/89 формально не
включал раздел,
посвященный встраиванию
конструкций SQL в программу
на традиционном языке
программирования. Этот
раздел являлся
приложением и, кроме того,
не включал правил
встраивания для языков Си
и Ада. В SQL/92 полностью
специфицированы правила
встраивания для наиболее
распространенных языков
программирования (Ада, Си,
Кобол, Фортран, MUMPS,
Паскаль, ПЛ/1).
4.2.1.12 ИНТЕРАКТИВНЫЙ
(ПРЯМОЙ) SQL
В SQL/92 специфицирован
набор операторов SQL,
которые должны
поддерживаться в
интерактивном режиме, хотя
некоторые решения
по-прежнему отдаются на
откуп реализациям.
Этим не исчерпываются
расширения SQL/92 по
сравнению с SQL/89. Однако не
упомянутые выше новые
возможности SQL не кажутся
настолько важными, чтобы о
них стоило писать в
журнальной статье (может
быть, я и ошибаюсь...).
4.2.2 Несовместимости
В стандарте SQL/92
содержится приложение, в
котором устанавливаются
несовместимости между SQL/92
и SQL/89. Формально SQL/92 не
включает в себя полностью
SQL/89, т.е. некоторые
конструкции SQL/89 не
соответствуют стандарту
SQL/92. Конечно, при переходе
от SQL/89 к SQL/92 нужно
внимательно отнестись к
этим несоответствиям.
Однако, во-первых, эти
несоответствия являются
слишком техническими и
непринципиальными, чтобы
описывать их в этой статье.
Во-вторых, ни один
производитель СУБД
никогда не пойдет на то,
чтобы с использованием его
нового продукта перестали
работать ранее
разработанные прикладные
системы. Поэтому можно
быть почти уверенным (или
уверенной), что в
реализациях SQL/89
по-прежнему будет
поддерживаться. Так что по
поводу несоответствий мы
отсылаем читателя к тексту
стандарта SQL/92.
5. Сводка
возможностей SQL-3
В стандарте SQL/92 по
сравнению со стандартом
SQL/89 язык был расширен
главным образом
количественно, хотя даже
этих количественных
расширений оказалось
достаточно для того, чтобы
стандарт SQL/92 не удалось
полностью реализовать до
сих пор в большинстве
коммерческих СУБД.
Поскольку SQL/92 не
удовлетворял значительной
части претензий,
исторически предъявляемых
к языку SQL, был сформирован
новый комитет, который
должен выработать
стандарт языка с
качественными
расширениями. Язык SQL-3 пока
не сформирован полностью,
многие аспекты продолжают
обсуждаться. Поэтому к
приводимой здесь сводке
возможностей нужно
относиться как к сугубо
предварительной.
5.1 Типы
данных
Набор встроенных типов
данных предполагается
расширить типами BOOLEAN и
ENUMERATED. Хотя по причине
поддержки неопределенных
значений языку SQL
свойственно применение
трехзначной логики, тип
BOOLEAN содержит только два
возможных значения true и
false. Для представления
значения unknown
рекомендуется
использовать NULL, что,
конечно, не вполне
естественно.
Перечисляемый тип ENUMERATED
обладает свойствами,
подобными свойствам
перечисляемых типов в
языках программирования.
Расширены возможности
работы с неопределенными
значениями. Появился новый
оператор CREATE NULL CLASS,
позволяющий ввести
именованный набор
именованных
неопределенных значений.
При определении домена
можно явно указать имя
класса неопределенных
значений, появление
которых допустимо в
столбцах, связанных с этим
доменом. Смысл каждого
неопределенного значения
интерпретируется на
уровне пользователей.
Предполагается
включение в язык
возможности использовать
определенные
пользователями типы
данных. Видимо, будут
иметься возможности
определения абстрактных
типов данных с произвольно
сложной внутренней
структурой на основе таких
традиционных спецификаций
агрегирования и
структуризации, как LIST, ARRAY,
SET, MULTISET и TURPLE, а также
возможности определения
объектных типов с
соответствующими методами
в стиле
объектно-ориентированного
подхода.
Появляется возможность
использования принципов
наследования свойств
существующей таблицы
(супертаблицы) при
определении новой таблицы
(подтаблицы). Подтаблица
наследует от супертаблицы
все определения столбцов и
первичного ключа. Другая
возможность - создать
таблицу, "подобную"
существующей в том смысле,
что в новой таблице
наследуются определения
некоторых столбцов
существующей таблицы.
5.2 Некоторые
другие свойства SQL-3
Одной из проблем
реализации языка SQL всегда
являлась проблема
распознавания
"изменяемости"
соединений. Как известно,
если представление
включает соединение
общего вида, то
теоретически невозможно
определить, можно ли
однозначно
интерпретировать операции
обновления такого
представления. Однако
существует несколько
важных классов соединений,
которые заведомо являются
изменяемыми. В SQL-3
предполагается выделить
эти классы с помощью
специальных
синтаксических
конструкций.
Наконец-то появляется
возможность определения
триггеров как комбинации
спецификаций события и
действия. Действие
определяется как
SQL-процедура, в которой
могут использоваться как
операторы SQL, так и ряд
управляющих конструкций.
На самом деле этот
механизм очень близок к
тому, который реализован в
Oracle V.7.
Что касается управления
транзакциями, то
происходит возврат к
старой идее System R о
возможности установки
внутри транзакции точек
сохранения (savepoints). В
операторе ROLLBACK можно
указать идентификатор
ранее установленной точки
сохранения, и тогда будет
произведен откат
транзакции не к ее началу,
а к этой точке сохранения.
Как видно, можно ожидать
наличия в SQL-3 многих
интересных и полезных
возможностей. Однако даже
промежуточные проекты
стандарта включают почти в
два раза больше страниц,
чем стандарт SQL/92. Поэтому
трудно ожидать быстрой
реализации этого
стандарта после его
принятия (а многие вообще
сомневаются, что этот
стандарт будет когда-либо
реализован).
6. Заключение
Наверное, многим
читателям эта статья
показалась очень скучной.
Что поделаешь, трудно
писать увлекательно, когда
речь идет о стандартах. Они
скучны, но очень полезны.
Конечно, язык SQL не
относится к семейству
наиболее красивых,
элегантных, понятных и
приятных изобретений
человечества. У SQL, видимо,
больше недругов, чем
друзей. Тем не менее именно
этот язык лежит в основе
современных систем
управления базами данных,
и в ближайшем будущем эта
ситуация сохранится. (На
самом деле, появляется
ощущение, что полностью от
стиля SQL не удастся
освободиться уже никогда.)
Поэтому, уважаемые дамы и
господа, давайте
основательно осваивать
стандарты языка SQL и
проектировать и
разрабатывать реально
переносимые
информационные системы.