В стандарте 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 предоставляет възможность производства более "самосъдържащихся" приложных програм. Имеется больше оснований рассчитывать на простоту переноса такой програмы в среду другой СУБД, поскольку стандарт встраивания более или менее соблюдается. Основным недостатком является някоиый PL-подобный вид таких програм, независимо от выбранного основного езика. И конечно, нужно учитывать замечания, съдържащиеся в предыдущих абзацах.
Далее мы коротко опишем език модулей и правила встраивания в соответствии со стандартом SQL/89 (еще раз заметим, что формально правила встраивания не являются частью стандарта).
16.2. Език модулей
Структура модуля SQL в стандарте SQL/89 определяется следующими синтактическими правилами:
Съществено, что всеки модуль SQL ориентирован на използование в програмах, написанных на конкретном езике програмирования. Ако в модуле присутствуют процедуры работы с курсорами, то все курсоры должны быть специфицированы в начале модуля. Заметим, что объявление курсора не погружается в какую-либо процедуру, поскольку это описательный, а не выполняемый оператор SQL.
Процедуры в модуле SQL определяются следующими синтактическими конструкциями:
::=
PROCEDURE
...;
;
::=
| ::= SQLCODE
::=
| | | | | | | |
Имена всех процедур в одном модуле должны быть различны. Любое имя параметра, съдържащегося в операторе SQL процедуры, должно быть специфицировано в разделе объявления параметров. Число фактических параметров при извикванее процедуры должно совпадать с числом формальных параметров, указанных при ее объявлении. Список формальных параметров каждой процедуры должен съдържать ровно один параметр SQLCODE (код ответа процедуры; възможные значения кодов ответа стандартизованы, но некоито из них определяются в реализации).
16.3. Встроенный SQL
Поскольку в стандарте SQL/89 не специфицированы (даже в приложениях) правила встраивания SQL в език Си, мы приведем только общие синтактические правила встраивания, използуемые за любого езика. Это поможет оценить "степень стандартности" конкретной реализации.
::=
{ | | }
[]
::= EXEC SQL
::= END EXEC | ;
::=
(...]
::=
BEGIN DECLARE SECTION []
::=
END DECLARE SECTION []
::= : ::=
WHENEVER ::= SQLERROR | NOT FOUND
::= CONTINUE | ::= { GOTO | GO TO } ::= : |
Встраиваемые операторы SQL, включая объявления курсора, а также разделы объявления исключительных ситуаций и переменных основной програмы, должны быть окружены скобками EXEC SQL и END EXEC. Объявление курсора должно встречаться текстуально раньше любого оператора, ссылающегося на этот курсор. Все променливи основной програмы, използуемые во встроенных операторах SQL, должны быть объявлены в текстуально предшествующем этому оператору разделе объявления переменных основной програмы. При этом синтаксис объявления переменной соответствует синтаксису основного езика програмирования, но имени переменной предшествует двоеточие.
Механизм обработки исключительных ситуаций в SQL/89 исключительно прост (можно сказать, примитивен). Можно задавать реакцию на возникновение двух видов условий: SQLERROR - это условие появления в переменной SQLCODE после изпълнения встроенного оператора отрицательного значения; NOT FOUND - условие появления в SQLCODE значения +100 (этот код означает исчерпание курсора). Реакция может состоять в изпълнении безусловного перехода на метку основной програмы (действие GO TO), или отсутствовать (действие CONTINUE). Срабатывает тот оператор определения реакции на исключительную ситуацию, который текстуально ближе от начала програмы к данному оператору SQL.
Заметим, что во многих реализациях поддържася два вида кодов ответа при изпълнении операторов SQL (встроенных или взятых из модуля): через переменную SQLCODE с кодами ответа, представляемыми целыми числами и через переменную SQLSTATE с кодами ответа, кодируемыми десятичными числами, представленными в текстовой форме. Имеется тенденция к переходу на използование только механизма SQLSTATE, но в стандартных реализациях должен поддерживаться механизм SQLCODE.
16.4. Набор операторов манипулирания данными
В стандарте SQL/89 определен очень ограниченный набор операторов манипулирания данными. Их можно классифицировать на группы операторов, свързанных с курсором; одиночных операторов манипулирания данными; и операторов завершения транзакции. Все эти операторы можно използовать как в модулях SQL, так и во встроенном SQL. Заметим, что в SQL/89 не определен набор операторов интерактивного SQL.
16.4.1. Операторы, свързанные с курсором
Операторы этой группы объединяет то, что все они работают с някоиым курсором, объявление которого должно съдържаться в том же модуле или програме со встроенным SQL.
За удобства мы повторим здесь синтактические правила объявления курсора, приводившиеся раньше:
::=
DECLARE CURSOR FOR ::=
[...]
::=
| UNION [ALL] ::= | ()
::=
ORDER BY [{,}...]
::=
{ | }
[ASC | DESC]
В объявлении курсора могут задаваться заявки наиболее общего вида с възможностью изпълнения операции UNION и сортировкой конечного резултата. Этот оператор не является выполняемым, он только связывает имя курсора со спецификацией курсора.
Оператор открытия курсора
Оператор описывается следующим синтактическим правилом:
::= OPEN
В реализациях встроенного SQL обикновено требуется, чтобы объявление курсора текстуально предшествовало оператору открытия курсора. Оператор открытия курсора должен быть первым в серии выполняемых операторов, свързанных с заданным курсором. При изпълнении этого оператора се извършва подготовка курсора к работе над ним. В частности, в этот момент се извършва связывание спецификации курсора со значениями переменных основного езика в случае встроенного SQL или параметров в случае модуля.
В большинстве реализаций в случае встроенного SQL именно изпълнение оператора открытия курсора приводит к компилации спецификации курсора.
Следующие операторы можно выполнять в произвольном порядке над отвореним курсором.
Оператор чтения очередной строки курсора
Синтаксис оператора чтения следующий:
::=
FETCH INTO ::=
[{,}...]
В операторе чтения указывается имя курсора и обязательный раздел INTO, съдържащий список спецификаций назначения (список имен переменных основной програмы в случае встроенного SQL или имен "выходных" параметров в случае модуля SQL). Число и типы от данни в списке назначений должны совпадать с числом и типами от данни списка выборки спецификации курсора.
Любой отворений курсор всегда имеет позицию: он може да бъде установлен перед някоиой строкой результирующей таблицы (перед первой строкой сразу после открытия курсора), на някоиую строку резултата или за последней строкой резултата.
Ако таблица, на которую указывает курсор, является пустой, или курсор позиционирован на последнюю строку или за ней, то при изпълнении оператора чтения курсор устанавливается в позицию после последней строки, параметру SQLCODE присваивается значение 100, никакие значения не присваиваются целям, идентифицированным в разделе INTO.
Ако курсор установлен в позицию перед строкой, то он устанавливается на эту строку, и значения этой строки присваиваются соответствующим целям.
Ако курсор установлен на строку r, отличную от последней строки, то курсор устанавливается на строку, непосредственно следующую за строкой r, и значения из этой следующей строки присваиваются соответствующим целям.
Въниква естественный вопрос, каким образом можно параметризовать курсор неопределенным значением или узнать, что выбранное из очередной строки значение является неопределенным. В SQL/89 это достигается за счет използования так называемых индикаторных параметров и переменных. Ако известно, что значение, передаваемое из основной програмы СУБД или принимаемое основной програмой от СУБД, може да бъде неопределенным, и этот факт интересует приложного програмиста, то спецификация параметра или переменной в операторе SQL имеет вид:
[INDICATOR]
при спецификации параметра, и [INDICATOR] при спецификации переменной. Отрицательное значение индикаторного параметра или индикаторной переменной (они должны быть целого типа) соответствует неопределенному значению параметра или переменной.
Оператор позиционного удаления
Синтаксис этого оператора следующий:
::=
DELETE FROM
WHERE CURRENT OF
Ако указанный в операторе курсор открыт и установлен на някоиую строку, и курсор определяет изменяемую таблицу, то текущая строка курсора удаляется, а он позиционируется перед следующей строкой. Таблица, указанная в разделе FROM оператора DELETE, должна быть таблицей, указанной в самом внешнем разделе FROM спецификации курсора.