
- •Isbn 5-8459-0138-3 (рус) isbn 0-201-38590-2 (англ)
- •Глава 2. Архитектура системы баз данных 65
- •Глава 6. Реляционная алгебра 192
- •Глава 7. Реляционное исчисление 243
- •Глава 8. Целостность данных 301
- •Глава 9. Представления 350
- •Часть 111
- •Часть IV
- •Глава 14. Восстановление 544 14.1. Введение 544
- •Глава 15. Параллельность 566
- •Часть V
- •Глава 16. Защита данных 602
- •Глава 17. Оптимизация 639
- •Глава 18. Отсутствующая информация 693
- •Глава 19. Наследование типов 725
- •Глава 20. Распределенные базы данных 767
- •Глава 21. Поддержка принятия решений 813
- •Глава 22. Хронологические базы данных 853
- •Глава 23. Логические системы управления базами данных 899
- •Часть VI
- •Глава 24. Объектные базы данных 944
- •Глава 25. Объектно-реляционные базы данных 999
- •Часть I (четыре главы) — это обширное введение в теорию баз данных вообще и реляционных баз данных в частности. Здесь также излагаются основы стандартно- го языка баз данных sql.
- •Часть IV. Две главы данной части — это несколько пересмотренные и расширен- ные версии глав 13 и 14 предыдущего издания.
- •Часть VI. Глава 24 является полностью переписанной и значительно улучшенной версией глав 22-24. Глава 25 почти полностью обновлена.
- •Часть I
- •Часть I состоит из четырех вводных глав.
- •1.1. Вводный пример
- •1.2. Что такое система баз данных
- •1.3. Что такое база данных Перманентные данные
- •1.4. Назначение баз данных
- •1.5. Независимость данных
- •1.6. Реляционные и другие системы
- •1.7. Резюме
- •2.1. Введение
- •2.2. Три уровня архитектуры
- •Внешний уровень (представления отдельных пользователей)Концептуальный уровень (обобщенное представление пользователей)
- •2.3. Внешний уровень
- •Отображение "внешний/концептуальный" схемы
- •Определение структур хранения (внутренняя схема)
- •Внешнее представление а Концептуальная схема
- •2.4. Концептуальный уровень
- •2.5. Внутренний уровень
- •2.6. Отображения
- •2.7. Администратор базы данных
- •2.8. Система управления базой данных
- •2.9. Система управления передачей данных
- •2.10. Архитектура "клиент/сервер"
- •2.11. Утилиты
- •2.12. Распределенная обработка
- •2.13. Резюме
- •3.1. Введение
- •3.2. Реляционная модель
- •3.3. Отношения и переменные-отношения
- •3.4. Смысл отношений
- •3.5. Оптимизация
- •3.6. Каталог
- •3.7. Базовые переменные-отношения и представления
- •3.8. Транзакции
- •3.9. База данных поставщиков и деталей
- •3.10. Резюме
- •Глава 4
- •4.1. Введение
- •4.2. Обзор языка sql
- •4.3. Каталог
- •4.4. Представления
- •4.5. Транзакции
- •4.6. Внедрение sql-операторов
- •4.7. Несовершенство языка sql
- •4.8. Резюме
- •Часть 9. Управление внешними данными (sql/med) Часть 10. Связь с объектным языком (sql/olb)
- •Часть II
- •Глава 5
- •5.1. Введение
- •5.2. Домены
- •5.3. Значения отношений
- •5.4. Переменные-отношения
- •5.5. Средства sql
- •5.6. Резюме
- •6.1. Введение
- •6.2. Реляционная замкнутость
- •6.3. Синтаксис
- •6.4. Семантика
- •6.5. Примеры
- •6.5.1. Получить имена поставщиков детали с номером 'р2'
- •6.5.2. Получить имена поставщиков по крайней мере одной красной детали
- •6.5.3. Получить имена поставщиков всех типов деталей
- •6.5.4. Получить номера поставщиков по крайней мере тех типов деталей, которые поставляет поставщик с номером 's2'
- •6.5.5. Получить все пары номеров поставщиков, находящихся в одном городе
- •6.5.6. Получить имена поставщиков, которые не поставляют деталь с номером 'р2'
- •6.6. Зачем нужна реляционная алгебра
- •6.7. Дополнительные операторы
- •6.8. Группирование и разгруппирование
- •6.9. Реляционные сравнения
- •6.10. Резюме
- •7.1. Введение
- •7.2. Исчисление кортежей
- •7.3. Примеры
- •7.3.5. Найти имена поставщиков по крайней мере одной детали, поставляемой поставщиком с номером 's2'
- •7.3.6. Выбрать имена поставщиков всех типов деталей
- •7.3.7. Определить имена поставщиков, которые не поставляют деталь с номером 'р2'
- •7.3.8. Определить номера поставщиков по крайней мере всех типов деталей, поставляемых поставщиком с номером *s2'
- •7.4. Сравнительный анализ реляционного исчисления и реляционной алгебры
- •7.5. Вычислительные возможности
- •7.5.1. Определить номера и вес в граммах всех типов деталей, вес которых превышает 10 ооо г
- •7.6.1. Выбрать номера поставщиков из Парижа со статусом, большим 20
- •7.7.1. Указать цвета деталей и названия городов, в которых находятся детали "не из Парижа" с весом, превышающим 10 фунтов
- •7.7.2. Для всех деталей указать номер и вес в граммах
- •7.7.3. Выбрать информацию обо всех парах поставщиков и деталей, находящихся в одном городе
- •7.7.4. Найти все пары названий городов, таких, что поставщик из первого города поставляет деталь, находящуюся во втором городе
- •7.7.5. Выбрать все пары номеров поставщиков, таких, что оба поставщика в каждой паре находятся
4.4. Представления
Приведем пример определения представления на языке SQL.
CREATE VIEW GOOD SUPPLIER
AS SELECT Si, STATUS, CITY FROM S
WHERE STATUS > 15 ;
А вот пример SQL-запроса к этому представлению.
SELECT Si, STATUS FROM GOOD_SUPPLIER WHERE CITY = 'London' ;
Подставив определение представления вместо ссылки на имя представления, получим выражение, которое будет подобно приведенному ниже (обратите внимание на вложен- ный подзапрос в предложении FROM),
SELECT GOOD SUPPLIER.Sf, GOOD SUPPLIER.STATUS FROM ( SELECT St, STATUS, CITY FROM S
WHERE STATUS > 15 ) AS GOODJUPPLIER WHERE GOODJUPPLIER.CITY = 'London' ;
Это выражение может быть затем упрощено, например, так.
SELECT St, STATUS FROM S
WHERE STATUS > 15
AND CITY = 'London' ;
В последнем случае показан текст запроса, который фактически будет выполняться. В качестве второго примера рассмотрим следующую операцию DELETE.
FROM GOOD SUPPLIER WHERE CITY~= 'London' ;
Запрос на удаление, который будет выполняться на самом деле, выглядит так.
DELETE FROM S
WHERE STATUS > 15
AND CITY = 'London' ;
4.5. Транзакции
Для операторов COMMIT и ROLLBACK в языке SQL есть прямые аналоги. Это операторы COMMIT WORK и ROLLBACK WORK соответственно (в обоих операторах слово WORK необяза- тельное). Но в языке SQL нет явного оператора, соответствующего оператору BEGIN TRANSACTION. Неявно транзакция начинается всякий раз, когда программа выполняет оператор, способный "инициализировать транзакцию" (transaction-initiating), но только в том случае, когда никакая транзакция еще не выполняется. Рассматривать здесь список SQL-операторов, способных инициализировать транзакцию, в наши планы не входит. Достаточно сказать, что таковыми являются практически все операторы, которые мы об- суждаем в этой главе (кроме, конечно, самих операторов COMMIT и ROLLBACK).
4.6. Внедрение sql-операторов
В большинстве SQL-продуктов операторы языка SQL могут выполняться как непосред- ственно (т.е. интерактивно, с подключенного терминала), так и в виде части прикладной программы (т.е. SQL-операторы могут быть внедренными, а значит, могут смешиваться с
операторами базового языка этой программы). Приложения, использующие внедренные SQL-операторы, могут быть написаны на многих базовых языках: COBOL, Java, PL/I и т.д.4 Рассмотрим особенности технологии внедрения SQL-операторов более подробно.
Фундаментальный принцип, лежащий в основе технологии внедрения SQL- операторов, мы будем называть принципом двухрежимности. Он заключается в том, что любое SQL-выражение, которое можно использовать интерактивно, можно при- менять и в прикладной программе. Конечно, существует множество различий в деталях между интерактивными SQL-операторами и их внедренными аналогами. В частности, операции выборки требуют существенной дополнительной обработки в вычислительной среде базового языка (подробности приводятся ниже в этом же разделе). Тем не менее сам принцип двухрежимности всегда соблюдается. (Обратное правило, между прочим, не верно, т.е. существует несколько внедряемых SQL-операторов, которые не могут ис- пользоваться интерактивно, в чем мы вскоре убедимся.)
Прежде чем начать обсуждение конкретных внедряемых SQL-операторов, необходи- мо обсудить некоторые детали. Большинство из них иллюстрируется фрагментом про- граммы, представленным на рис. 4.3. (Для закрепления наших представлений будем счи- тать, что базовым языком является PL/I. Большинство приводимых примеров транслиру- ется на другие базовые языки лишь с незначительными изменениями.)
EXEC SQL BEGIN DECLARE SECTION ;
DCL SQLSTATE CHAR(5) J DCL Pi CHAR(6) ;
DCL WEIGHT FIXED DECIMAL(5,1) ?
EXEC SQL END DECLARE SECTION }
Pi = 'P2' ; /* например */
EXEC SQL SELECT P.WEIGHT
INTO !WEIGHT
FROM P
WHERE P.Pi = :Pi ; IF SQLSTATE = '00000'
THEN ... ; /* WEIGHT = <выбираемое значение> */
ELSE ... ; /* возникла какая-то исключительная ситуация */
Рис. 4.3. Фрагмент программы на языке PL/1 с внедренными операторами языка SQL Рассмотрим все по порядку.
4 Стандарт языка SQL [4.22] в настоящее время поддерживает языки Ada, С, COBOL, Fortran, М (раньше он назывался MUMS), Pascal и PL/I. Требование поддержки языка Java на время написания книги еще отсутствовало, но должно было быть добавлено в ближайшее время (подробности приводятся в [4.6], а также в приложении Б), причем некоторые продукты уже поддерживают язык Java.
1. Внедренные SQL-операторы предваряются инструкцией EXEC SQL, так что их лег- ко отличить от других операторов базового языка, и заканчиваются специальным завершающим символом (точка с запятой ";" для языка PL/I).
Выполняемый SQL-оператор (далее до конца этого раздела уточнение "внедренный" обычно будет опускаться) может быть в программе везде, где могут быть выполняемые операторы базового языка. Обратите внимание на уточнение "выполняемые": в отличие от интерактивного режима использования языка SQL, режим внедрения SQL-операторов подразумевает включение в программу отдель- ных SQL-операторов, которые являются чисто декларативными, а не выполняемы- ми. Например, оператор DECLARE CURSOR— это невыполняемый оператор (подробности приводятся в разделе "Операции, использующие курсоры"); таковы- ми не являются и операторы BEGIN и END DECLARE SECTION (см. п. 5 этого списка), а также оператор WHENEVER (см. п. 9).
SQL-операторы могут включать ссылки на базовые переменные (т.е. переменные базового языка). Подобные ссылки должны включать префикс в виде двоеточия, предназначенный для отличия их от имен столбцов SQL-таблиц. Базовые перемен- ные могут применяться во внедренных SQL-операторах везде, где в интерактивном языке SQL могут использоваться литералы. Они могут также находиться в предло- жении INTO операторов SELECT (см. п. 4) и FETCH (подробности — в разделе "Операции, использующие курсоры"), определяющих результирующие перемен- ные для размещения результатов выборки данных.
Обратите внимание на предложение INTO оператора SELECT, представленного на рис. 4.3. Назначение этого предложения (как только что отмечалось) — указать ре- зультирующие (целевые) переменные, в которые будут возвращены выбранные зна- чения. Каждая /-я целевая переменная, указанная в предложении INTO, соответствует <-му извлекаемому значению, указанному в списке выборки предложения SELECT.
Все базовые переменные, на которые ссылаются внедренные SQL-операторы, должны быть определены (в PL/I это оператор DCL) в разделе объявлений вне- дренного языка SQL, который ограничивается операторами BEGIN DECLARE SECTION и END DECLARE SECTION.
Каждая программа, содержащая внедренные SQL-операторы, должна включать ба- зовую переменную с именем SQLSTATE. После выполнения любого присутствую- щего в программе SQL-оператора в эту переменную возвращается код состояния. В частности, код состояния 00000 означает, что оператор был выполнен успешно, а код состояния 02000 — что оператор был выполнен, но никаких удовлетворяющих запросу данных найдено не было. Таким образом, выполнение в программе каждо- го SQL-оператора должно завершаться проверкой значения переменной SQLSTATE и, если это значение будет отличаться от ожидаемого, должны предприниматься соответствующие действия. На практике, однако, такая проверка обычно выполня- ется неявно (см. п. 9).
Базовые переменные должны иметь типы данных, соответствующие значениям, для размещения которых эти переменные используются. В частности, базовая пе- ременная, используемая в качестве целевой (т.е. для размещения результатов опе- рации SELECT), должна иметь тип данных, который совместим с типом выражения, представляющего значение, присваиваемое этой целевой переменной. Аналогично, если базовая переменная служит источником (например, для операции INSERT), она должна иметь тип данных, совместимый с SQL-типом того столбца, которому при-
сваивается значение из этого источника. Подобные замечания касаются также ба- зовых переменных, используемых в сравнениях или в любых других операциях. В официальном документе стандарта [4.22] подробно объясняется, что для двух ти- пов значит быть совместимыми.
Базовые переменные для столбцов SQL-таблиц могут иметь те же имена, что и имена соответствующих столбцов.
Как уже упоминалось, выполнение каждого SQL-оператора, в принципе, должно сопровождаться проверкой значения, возвращаемого в переменной SQLSTATE. Для упрощения этого процесса предназначен оператор WHENEVER, который имеет сле- дующий синтаксис.
EXEC SQL WHENEVER <условие> <действие> ;
Здесь параметр <условие> может принимать значение либо SQLERR0R (ошибка SQL), либо NOT FOUND (не найдено), а параметр <действие>— это либо оператор CONTINUE (продолжить), либо оператор GO ТО (перейти к). Оператор WHENEVER не является выполняемым; это просто директива для SQL-компилятора. Наличие в программе выражения "WHENEVER <условие> GO ТО <метка>" приведет к тому, что компилятор поместит оператор "IF <условие> GO ТО <метка> END IF" после каж- дого встретившегося ему выполняемого SQL-оператора. Однако, встретив выраже- ние "WHENEVER <условие> CONTINUE", SQL-компилятор не вставляет в программу никаких операторов и, следовательно, программист должен будет вставить требуе- мые операторы вручную. Два фиксированных значения условия определяются так.
NOT FOUND означает никаких данных не найдено
SQLSTATE = 02000 (обычно) SQLERROR означает возникла ошибка
см. описание стандарта [4.22] для переменной SQLSTATE
Каждый оператор WHENEVER, который процессор SQL встречает при последователь- ном сканировании текста программы (для определенного условия), отменяет пре- дыдущий (для этого условия).
10. Используя терминологию главы 2, отметим, что внедрение SQL-операторов уста- навливает слабую связь между SQL-средой и базовым языком.
Итак, для предварительного обсуждения этого достаточно. Далее мы сосредоточимся на операторах манипулирования данными. Как уже отмечалось, большинство из них можно использовать практически в неизменном виде (т.е. лишь с незначительными из- менениями в синтаксисе). Однако операции выборки требуют особого рассмотрения. Проблема состоит в том, что такие операторы в общем случае выбирают не одну, а мно- жество строк, в то время как процедурные базовые языки обычно не приспособлены для выборки более одной строки за одно обращение. Следовательно, необходимо обеспечить своего рода "мост" между уровнем выборки "множество-за-один-раз" в языке SQL и уровнем выборки "строка-за-один-раз" в базовом языке. В качестве подобного моста ис- пользуются курсоры. Курсор — это специальный тип SQL-объекта, который применяет- ся только во внедренном языке SQL (поскольку в интерактивном языке SQL в нем нет необходимости). Курсор представляет собой определенный тип логического указателя, который может использоваться для перемещения по набору строк, указывая поочередно
на каждую из них и таким образом обеспечивая возможность адресации к этим строкам — к одной за один раз. Однако временно отложим подробное обсуждение кур- соров и рассмотрим сначала такие операторы, для которых курсоры не требуются.
Операции, не использующие курсоры
Ниже перечислены операторы манипулирования данными, для которых не требуется использование курсоров.
Однострочный оператор SELECT
INSERT
UPDATE(кроме формы CURRENT)
DELETE (также кроме формы CURRENT) Рассмотрим примеры для каждого из этих операторов.
Однострочный оператор SELECT. Получить статус и название города для поставщи- ка, номер поставки которого задан в базовой переменной GIVENSi.
EXEC SQL SELECT STATUS, CITY INTO .-RANK, :CITY FROM S
WHERE Si = :GIVENSi ;
Термин однострочный оператор SELECT используется для обозначения выражения SELECT, значением которого будет таблица, содержащая не более одной строки. В дан- ном примере, если в таблице S существует ровно одна строка, удовлетворяющая задан- ному условию WHERE, значения столбцов STATUS и CITY из этой строки в соответствии с запросом будут присвоены базовым переменным RANK и CITY, а переменной SQLSTATi будет присвоено значение 00000. Если в таблице S нет ни одной строки, удовлетворяю- щей заданному условию WHERE, переменной SQLSTATE будет присвоено значение 02000 Если же таких строк окажется больше одной, будет зафиксирована ошибка и nepeMeHHaf SQLSTATE будет содержать ее код.
Оператор INSERT. Вставить в таблицу Р сведения о новой детали (номер детали, ее название и вес задаются содержимым базовых переменных Р|, PNAME, PWT соответствен- но; цвет детали и город неизвестны).
EXEC SQL INSERT
INTO P ( Pi, PNAME, WEIGHT J VALUES ( :Pi, :PNAME, :PWT ) ;
Столбцам COLOR и CITY вновь добавляемой строки таблицы будут присвоены значения принимаемые по умолчанию. Подробнее об этом речь пойдет в разделе 5.5 главы 5.
Оператор UPDATE. Увеличить статус всех поставщиков из Лондона на значение, по мещенное в базовую переменную RAISE.
EXEC SQL UPDATE S
SET STATUS = STATUS + :RAISE WHERE CITY = 'London' ;
Если в таблице поставщиков строк, удовлетворяющих условию WHERE, найдено не бу- дет, система присвоит переменной SQLSTATE значение 02000.
Оператор DELETE. Удалить сведения обо всех поставках для поставщиков из города, название которого помещено в базовую переменную CITY.
EXEC SQL DELETE
FROM SP WHERE :CITY =
( SELECT CITY FROM S
WHERE S.Sf = SP.St ) ;
И снова, если нет строк, удовлетворяющих условию WHERE, переменной SQLSTATE присваивается значение 02000. Также обратите внимание на вложенный подзапрос (на этот раз в предложении WHERE).
Операции, использующие курсоры
Теперь перейдем к вопросу о выборках на уровне множеств, т.е. о выборках не одной строки, как это было в случае однострочного оператора SELECT, а множества с произ- вольным количеством строк. Как указывалось ранее, в этой ситуации потребуется пооче- редный доступ к строкам выбранного множества, а механизмом такого доступа будет курсор. На рис. 4.4 этот процесс схематически проиллюстрирован на примере выборки информации о поставщиках (столбцы Si, SNAME и STATUS) для всех поставщиков из горо- да, название которого задается в базовой переменной Y.
EXEC |
SQL DECLARE X CURSOR FOR /* определить курсор X */ |
|
SELECT S.Si, S.SNAME, S.STATUS |
|
FROM S |
|
WHERE S.CITY = :Y |
|
ORDER BY Si ASC ; |
EXEC |
SQL OPEN X; /* выполнить запрос */ |
|
DO <для всех строк S, доступных через Х> } |
|
EXEC SQL FETCH X INTO :Si, :SNAME, :STATUS ; |
|
/* выбрать следующего поставщика */ |
|
END ; |
EXEC |
SQL CLOSE X ; /* закрыть курсор X */ |
Рис. 4.4. Выборка нескольких строк
Пояснение. Оператор DECLARE X CURSOR... определяет курсор с именем X, связанный с табличным выражением (т.е. выражением, которое вычисляет таблицу). Табличное выражение определяется оператором SELECT, который является частью всего выражения DECLARE. Причем указанное табличное выражение не вычисляется в этом месте програм- мы, поскольку оператор DECLARE CURSOR — чисто декларативный. Табличное выражение
вычисляется только при открытии курсора (оператор OPEN X). Далее для выборки строк из результирующего множества, по одной за один раз, используется оператор FETCH, присваивающий извлеченные значения базовым переменным в соответствии со специ- фикациями в предложении INTO. (Для простоты базовым переменным присвоены имена, совпадающие с именами соответствующих столбцов таблицы базы данных. Обратите внимание, что в операторе SELECT при определении курсора нет своего предложения INTO.) Поскольку в результирующем наборе потенциально присутствует большое коли- чество строк, оператор FETCH обычно используется в некотором цикле (оператор DO,. .END в языке PL/I). Цикл будет повторяться до тех пор, пока не закончатся строки в результирующем наборе. При выходе из цикла курсор X закрывается (оператор CLOSE X).
А теперь рассмотрим курсоры и операции с ними более подробно. Курсор определя- ется с помощью оператора DECLARE CURSOR, общий вид которого следующий.
EXEC SQL DECLARE <имя курсора> CURSOR
FOR Табличное выражение> [ <упорядочение> ] ;
Для краткости несколько необязательных спецификаций в этом определении не ука- заны. Здесь параметр <иыя курсора>— это имя определяемого курсора. Полное опреде- ление табличного выражения, помещаемого в параметр <табличное выражение^ дано в приложении А. Необязательный параметр определения сортировки результата выборки <упорядочение> имеет следующий формат.
ORDER BY <список элементов>
Здесь параметр <список элементов> содержит список перечисленных через запятую элементов, по которым должно быть выполнено упорядочение извлекаемых строк. Спи- сок элементов не должен быть пустым, в каждом элементе списка должно содержаться имя столбца (заметьте, не уточненное), после которого может следовать необязательное служебное слово ASC (по возрастанию) или DESC (по убыванию). При опускании служеб- ного слова по умолчанию принимается порядок по возрастанию (ASC).
Замечание. Дадим определение термину список элементов, перечисленных через запятую (commalist). Пусть <хуг> обозначает произвольную синтаксическую катего- рию (т.е. то, что находится слева от некоторого правила вывода в нотации BNF). Тогда выражение <xyz commalisO (или <список xyz>) обозначает последовательность из нуля или более элементов <xyz>, в которой каждая пара элементов <xyz> разделена запятой (и, может быть, одним или несколькими пробелами). Обратим ваше внимание на то, что сокращение <список .,.> будет широко использоваться в приводимых далее син- таксических правилах (причем во всех синтаксических правилах, а не только в прави- лах языка SQL).
Как утверждалось ранее, оператор DECLARE CURSOR— декларативный, а не выпол- няемый. Он предназначен для объявления курсора с определенным именем и постоянно связанного с ним табличного выражения и типа упорядочения. Табличное выражение может включать ссылки на базовые переменные. Программа может содержать любое ко- личество операторов DECLARE CURSOR, каждый из которых, конечно, предназначен для определения разных курсоров.
Для работы с курсорами существует три выполняемых оператора: OPEN, FETCH и CLOSE.
■ Оператор OPEN имеет следующий формат. EXEC SQL OPEN <имя курсора>;
Он предназначен для открытия или активизации указанного курсора (который в данный момент не должен быть открыт). В результате его выполнения вычисля- ется связанное с этим курсором табличное выражение (причем для всех базовых переменных, упоминаемых в этом выражении, используются текущие значения). В результате идентифицируется определенное множество строк, которое стано- вится текущим активным набором для данного курсора. Курсор также уста- навливает исходную позицию в этом активном наборе, а именно — позицию пе- ред его первой строкой. (Активные наборы всегда рассматриваются как упоря- доченные, а значит, и понятие позиции имеет для них смысл5. Порядок опреде- ляется предложением ORDER BY; если же оно отсутствует, порядок строк уста- навливается системой.)
■ Оператор FETCH имеет следующий формат.
EXEC SQL <шя курсора> INTO <список ссылок да базовые переменные> ;
Он служит для перемещения позиции указанного курсора (который должен быть уже открыт) к следующей строке в его активном наборе с последующим присвоением значений столбцов этой строки базовым переменным, указанным в предложении INTO. Если при выполнении оператора FETCH следующей стро- ки нет, то никакие данные не выбираются и переменной SQLSTATE присваива- ется значение 02000.
■ Оператор CLOSE имеет следующий формат. EXEC SQL CLOSE <имя курсора> ;
Он служит для закрытия (деактивизации) указанного курсора (который должен быть в данный момент открыт). После его выполнения у курсора уже не будет ак- тивного набора. Однако в дальнейшем курсор вновь может быть открыт; при этом он опять получит активный набор — возможно, уже не такой, как раньше (в част- ности, если значения указанных в объявлении курсора базовых переменных к те- кущему моменту были изменены). Заметьте, что изменение этих переменных при открытом курсоре не окажет влияния на его активный набор.
Есть еще два оператора, в которых могут использоваться ссылки на курсоры, — это CURRENT-формы операторов UPDATE и DELETE. Если курсор (скажем, X) в данный момент позиционирован на определенную строку, то можно обновить или удалить эту "текущую строку курсора X", т.е. строку, на которую курсор X в данный момент позиционирован, как, например, показано ниже.
EXEC SQL UPDATE S
5 Сами no себе множества, конечно, не являются упорядоченными (глава 5), так что "активный набор" — это на самом деле не множество как таковое. Его лучше представлять в виде упорядоченного списка или массива (строк).
SET STATUS = STATUS + :RAISE WHERE CURRENT OF X ;
Замечание. Выражения UPDATE.. .WHERE CURRENT и DELETE.. .WHERE CURRENT будут недопустимы, если табличное выражение в объявлении курсора определено с участием необновляемого представления, созданного с помощью оператора CREATE VIEW (подробности приводятся в главе 9, раздел 9.6).
Динамический SQL
Динамический язык SQL состоит из набора функций поддержки внедрения SQL- операторов, предназначенных специально для создания обобщенных, оперативных и, возможно, интерактивных приложений. (Напомним, что, как отмечалось в главе 1, инте- рактивные приложения — это приложения, которые предоставляют пользователю доступ к базе данных с некоторого интерактивного терминала.) Рассмотрим, что должно делать типичное интерактивное приложение. Схематически оно должно выполнять (многократно) следующие действия.
Принять с терминала команду пользователя.
Проанализировать поступившую команду.
Сгенерировать соответствующие SQL-операторы для обращения к базе данных.
Возвратить сообщение и (или) полученные результаты на терминал.
Если набор команд пользователя, который программа может принять с терминала, достаточно мал (как, например, в случае обработки предварительных заказов мест на авиалиниях), то набор всех возможных выполняемых SQL-операторов также будет неве- лик и его можно будет непосредственно внедрить в программу. В этом случае действия на втором и третьем этапах будут состоять в логической проверке введенной команды с последующим переходом к той части программы, которая выполняет заранее предопре- деленные SQL-операторы. В противном случае, если набор вводимых команд достаточно разнообразен, было бы непрактично заранее предопределять и внедрять в программу все требуемые SQL-выражения для всех возможных команд. Вместо этого, вероятно, целе- сообразнее конструировать необходимые SQL-запросы динамически, а затем динамиче- ски же компилировать и выполнять сконструированные запросы. Средства динамическо- го языка SQL предназначены для поддержки этого процесса.
Существует два основных динамических SQL-оператора— PREPARE и EXECUTE. Их использование проиллюстрировано на следующем (нереальном по простоте, но доста- точно точном) примере.
DCL SQLSOURCE CHAR VARYING (65000) ;
SQLSOURCE = 'DELETE FROM SP WHERE QTY < 300' j EXEC SQL PREPARE SQLPREPPED FROM :SQLSOURCE ; EXEC SQL EXECUTE SQLPREPPED ;
Пояснения
1. Имя SQLSOURCE идентифицирует переменную языка PL/I типа символьной строки переменной длины, в которой программа каким-либо образом конструирует исход- ную форму (т.е. представление в виде символьной строки) некоторого SQL- оператора, в нашем конкретном примере — оператора DELETE.
Имя SQLPREPPED, напротив, идентифицирует переменную среды SQL, а не базового языка PL/I, которая будет (концептуально) использоваться для хранения скомпилиро- ванной формы SQL-оператора (исходная форма которого представлена в переменной SQLSOURCE). Конечно, имена SQLSOURCE и SQLPREPPED можно выбирать произвольно.
С помощью оператора присвоения SQLSOURCE =...; переменной SQLSOURCE при- сваивается исходная форма SQL-оператора DELETE. Конечно, на практике процесс конструирования такого исходного оператора будет значительно сложнее и, воз- можно, в нем будут использоваться ввод и анализ некоторых элементов запросов от конечного пользователя, выраженных на обычном языке или в другой, более "дружественной для пользователя" форме, чем обыкновенный язык SQL.
Оператор PREPARE извлекает это исходное выражение и подготавливает (т.е. ком- пилирует) его, создавая выполняемую версию извлеченного им оператора, сохра- няемую в переменной SQLPREPPED.
Оператор EXECUTE выполняет откомпилированную версию оператора из перемен- ной SQLPREPPED, в результате чего осуществляется собственно операция DELETE. Информация SQLSTATE выполненного оператора DELETE возвращается так же, как при выполнении аналогичного оператора обычным образом.
Обратите внимание, что, поскольку имя SQLPREPPED идентифицирует переменную языка SQL, а не PL/I, при его использовании в операторах PREPARE и EXECUTE двоеточие перед ним не указывается. Заметьте также, что подобные SQL-переменные не объявля- ются явно.
Описанный выше процесс в точности совпадает с процессом, который происходит, если SQL-выражения вводятся интерактивно. Во многих системах имеется некоторое подобие процессора SQL-запросов. Этот процессор в действительности — не что иное, как обобщенное интерактивное приложение, способное обрабатывать весьма широкий спектр вводимых команд, а именно — любой допустимый (или недопустимый!) оператор языка SQL. Для конструирования SQL-операторов, соответствующих вводимым пользо- вателем командам, для компиляции и выполнения сконструированных операторов и для возврата сообщений и результатов на терминал в нем используются именно средства ди- намического языка SQL.
Завершая этот раздел, кратко рассмотрим более позднее (1995) дополнение стандарта SQL, известное как SQL Call-Level Interface, или кратко — просто интерфейс CLI. Ин- терфейс CLI, в основном, строится на базе интерфейса Open Database Connectivity ком- пании Microsoft (ODBC). Благодаря интерфейсу CLI приложения, которые написаны на одном из базовых языков, могут выдавать запросы к базе данных, обращаясь к процеду- рам CLI, предоставляемым изготовителем. Затем эти процедуры, обязательно предвари- тельно связанные с данным приложением, используют динамический язык SQL для вы- полнения требуемых операций с базой данных от имени приложения. (Иными словами, с точки зрения СУБД процедуры CLI могут считаться просто другим приложением.)
Как видим, интерфейс SQL/CLI (и ODBC тоже) решает ту же задачу, что и динамиче- ский язык SQL, а именно — позволяет приложению предоставлять текст SQL-оператора лишь к тому времени, когда его непосредственно необходимо выполнять. Однако подход интерфейсов CLI и ODBC к решению этой задачи лучше, чем подход динамического языка SQL. Эти преимущества заключаются в следующем.
Во-первых, динамический SQL — это стандарт исходного кода. Поэтому для лю- бого приложения, которое использует динамический язык SQL, требуется какой- то SQL-компилятор, необходимый для обработки установленных стандартом опе- раций, таких как PREPARE, EXECUTE и т.д. Интерфейсом CLI, напротив, нормирова- ны лишь детали вызова процедуры (т.е., в основном, вызовов подпрограмм). Не требуется услуг никакого специального компилятора, достаточно использовать обычный компилятор стандартного базового языка. Поэтому приложение может распространяться (возможно, сторонними изготовителями программного обеспе- чения) в "сжатой" форме в виде объектного кода.
Во-вторых, такие приложения могут быть независимыми от типа СУБД, т.е. ин- терфейс CLI включает средства создания общих приложений (опять же, возможно, от сторонних изготовителей программного обеспечения), которые могут исполь- зоваться для нескольких различных типов СУБД вместо специальных для какой-то конкретной СУБД.
Такие интерфейсы, как CLI, ODBC и JDBC (вариант ODBC для языка Java), приобре- тают все более важное значение по причинам, которые будут обсуждаться (частично) в главе 20.