- •Часть 2. Реляционная модель.
- •Глава 4. Реляционные объекты данных: домены и отношения.
- •4.1. Вводный пример
- •4.2. Домены
- •4.3. Отношения
- •4.4. Виды отношений
- •4.5. Отношения и предикаты
- •4.6. Реляционные базы данных
- •4.7. Резюме
- •Глава 8.
- •8.1. Введение
- •8.2. Определение данных
- •8.3. Обработка данных: операции выборки
- •8.3.1. Получить цвета и города для деталей "не из Парижа" с весом, большим десяти
- •8.3.2. Для всех деталей получить номер детали и ее вес в граммах
- •8.3.3. Получить полную информацию обо всех поставщиках
- •8.3.4. Получить информацию обо всех парах поставщиков и деталей, совмещенных в одном городе
- •8.3.5. Получить все пары имен городов, таких что поставщик, находящийся в первом городе, поставляет деталь, хранящуюся во втором городе
- •8.3.6. Получить все пары номеров поставщиков, таких что оба поставщика в каждой паре размещаются в одном и том же городе
- •8.3.7. Получить общее число поставщиков
- •8.3.8. Получить максимальное и минимальное количество для детали р
- •8.3.9. Для каждой поставляемой детали получить номер детали и общее количество поставки
- •8.3.10. Получить номера для всех деталей, поставляемых более чем одним поставщиком
- •8.3.11. Получить имена поставщиков, поставляющих деталь р2
- •8.3.12. Получить имена поставщиков, поставляющих по крайней мере одну красную деталь
- •8.3.13. Получить номера поставщиков, статус которых меньше текущего максимального статуса в таблице s
- •8.3.14. Получить имена поставщиков, поставляющих деталь р2
- •8.3.15. Получить имена поставщиков, которые не поставляют деталь р2
- •8.3.16. Получить имена поставщиков, поставляющих все детали
- •8.3.17. Получить номера деталей, которые или весят более 16 фунтов, или поставляются поставщиком s2, или и то и другое
- •8.4. Обработка данных: операции обновления
- •Table-term
- •I join-table-expression
- •8.6. Условные выражения
- •8.7. Скалярные выражения
- •8.8. Встроенный sql
- •8.8.1. Единичный select. Получить статус и город для поставщика, чей номер поставки задан базовой переменной givens#
- •8.8.2. Insert. Вставить новую деталь в таблицу р (номер детали, ее назв. И вес определены базовыми переменными р#, pname, pwt соответственно, цвет и город неизвестны)
- •8.8.3, Update. Увеличить статус всех поставщиков из Лондона на значение, определенное базовой переменной raise
- •8.8.4. Delete. Удалить все поставки для поставщиков из города,
- •8.9. Резюме
8.7. Скалярные выражения
Скалярные выражения в SQL, по существу, простые. Поэтому мы удовлетворимся списком наиболее важных операторов, которые можно использовать в конструкциях таких выражений, и предложим несколько дополнительных замечаний для операторов case и cast, значение которых сразу, возможно, и не очевидно. Подчеркнем, что в таких выражениях также могут встречаться итоговые функции, поскольку они возвращают скалярный результат. Более того, табличные выражения, заключенные в скобки, могут быть также истолкованы как скалярные значения, если только они в результате вычисления сводятся к таблице с одной строкой и одним столбцом. Как указывалось ранее, эта возможность, которая была введена в SQL/92, представляет главное усовершенствование новой версии SQL по сравнению с первоначальным вариантом.
Вот список основных операторов в алфавитном порядке: арифметические операторы (+,-,*,/); BIT_LENGTH; CASE; CAST; CHARACTER_LENGTH; CURRENT_USER; LOWER; OCTET_LENGTH; POSITION; SESSION_USER; SUBSTRING; SYSTEM_USER;TRIM; UPPER; USER.
Ниже кратко описаны операторы case и cast.
Оператор CASE
Оператор case возвращает одно значение из указанного набора значений в зависимости от определенных условий. Например:
CASE
WHEN S. STATUS < 5 THEN 'Обращаться в последнюю очередь'
WHEN S.STATUS < 10 THEN 'Сомнительный'
WHEN S.STATUS < 15 THEN 'He очень хороший'
WHEN S.STATUS < 20 THEN 'Посредственный'
WHEN S.STATUS < 25 THEN 'Приемлемый'
ELSE 'Хороший'
END
Оператор CAST
Оператор cast преобразует определенное скалярное значение к определенному скалярному типу данных (возможно, к домену, определенному пользователем). Например:
CAST ( 'S8' AS S# )
Не все пары типов данных взаимно конвертируемы. Более подробно о том, какие преобразования допустимы и как именно они выполняются, можно узнать из [8.1].
8.8. Встроенный sql
Как отмечалось ранее, выражения SQL могут выполняться или интерактивно, или как часть прикладной программы (в этом случае выражения SQL физически встраиваются в исходный код программы, смешиваясь с выражениями базового языка). До сих пор последний случай не рассматривался и подразумевалось (где это имело значение), что язык используется интерактивно. Теперь рассмотрим встроенный SQL.
Фундаментальный принцип, лежащий в основе встроенного SQL, который мы будем называть двухрежимным принципом, заключается в том, что любое выражение SQL, которое можно использовать интерактивно, можно также использовать и в прикладной программе. Конечно, существует множество различий в деталях между интерактивными и встроенными выражениями SQL, а операции выборки, в частности, требуют определенной внутренней обработки в среде базового языка; но, несмотря на это, двухрежимный принцип всегда верен. (Обратное, между прочим, не верно, т.е. существует несколько операторов встроенного SQL, которые не могут использоваться интерактивно, как мы вскоре убедимся.)
Необходимо также отметить, что двухрежимный принцип применяется ко всему языку SQL, а не только к операциям обработки данных. Верно и то, что операции подъязыка обработки данных, несомненно, одни из наиболее часто используемых в контексте программирования, но нет ничего плохого, например, во встроенном в программу операторе create table, если приложение это допускает.
Прежде чем начать обсуждение конкретных операторов встроенного SQL, необходимо рассмотреть некоторые предварительные детали. Большинство из них иллюстрируется программным фрагментом на рис. 8.3. (Для констатации наших представлений будем считать, что базовым языком является PL/I. Большинство представлений транслируется на другие базовые языки лишь с незначительными изменениями.)
ЕХЕС SQL BEGIN DECLARE SECTION ;
DCL SQLSTATE CHAR(5) ;
DCL P# CHAR(6) ;
DCL WEIGHT FIXED DECIMAL(3) ;
ЕХЕС SQL END DECLARE SECTION ;
P# = 'P2' ; /*например*/
EXEC SQL SELECT P.WEIGHT
INTO :WEIGHT
FROM P
WHERE P.P# = :P# ;
IF SQLSTATE = '00000'
THEN ... ; /* WEIGHT (ВЕС) = выбираемому значению */
ELSE ... ; /* возникла какая-то исключительная ситуация */
Рис. 8.3. Фрагмент программыPL/1 с встроенным языком SQL
Рассмотрим все по порядку.
1. Операторы встроенного SQL предваряются инструкцией ЕХЕС SQL, так что их легко отличить от других операторов базового языка, и заканчиваются специальным завершающим символом (точка с запятой ";" для PL/1).
2. Выполняемый оператор SQL (здесь и далее термин "встроенный" будет обычно опускаться) может быть в программе везде, где могут быть выполняемые базовые операторы. Обратите внимание на уточнение "выполняемые": в отличие от интерактивного SQL, встроенный SQL включает отдельные операторы, которые являются чисто декларативными, а не выполняемыми. Например, оператор declare cursor— не выполняемый оператор, таковыми не являются и операторы begin, end, declare section, а также whenever (см. ниже).
3. Операторы SQL могут включать ссылки на базовые переменные (т.е. переменные встроенного, или базового, языка); такие ссылки должны включать префикс в виде двоеточия для отделения их от имен столбцов SQL.
Базовые переменные могут использоваться во встроенном языке SQL (но только в операторах DML) везде, где могут использоваться литералы в интерактивном языке SQL. Они могут также находиться в инструкции into операторов select или fetch (см. ниже), обозначая результирующие переменные для выборки, и в определенных операторах "динамического SQL" (также см. ниже).
4. Обратите внимание на инструкцию into оператора select на рис. 8.3. Назначение этой инструкции (как только что отмечалось) — указать результирующие (целевые) переменные, в которые будут возвращены выбранные значения; i-я целевая переменная, указанная в инструкции into, соответствует i-му значению, которое будет выбрано, как указано в инструкции select.
5. Все базовые переменные, на которые будут ссылаться операторы SQL, должны быть определены в разделе описаний встроенного SQL, который ограничивается операторами begin и end раздела описаний (declare section).
6. Каждая программа встроенного SQL должна включать базовую переменную, называемую sqlstate(/////) . После выполнения какого-либо оператора в такую переменную в программе возвращается код состояния; в частности, код состояния 00000 означает, что оператор выполнился успешно, а 02000- что оператор выполнился, но никаких данных, удовлетворяющих запрос, не было найдено. Таким образом, каждый оператор SQL в программе должен завершаться проверкой переменной sqlstate, а соответствующие действия должны предприниматься, если значение не то, которое ожидалось. На практике, однако, такая проверка обычно выполняется неявно.
//////Ранние версии SQL использовали переменную, называемую SQLCODE; переменная SQLSTATE была добавлена в SQL/92, а переменная SQLCODE теперь официально "осуждается", поскольку большинство ее значений (в отличие от SQLSTATE) определяется реализацией, вместо того чтобы быть предписанными стандартом./////
7. Базовые переменные должны иметь подходящие типы данных в соответствии с тем, как эти переменные используются. В частности, базовая переменная, используемая в качестве целевой (например, для операции fetch), должна иметь тип данных, совместимый с типом выражения, которое представляет значение, присваиваемое этой целевой переменной; точно так же, если базовая переменная служит источником (например, для операции update), она должна иметь тип данных, совместимый с типом SQL того столбца, которому присваивается значение этого источника. Подобные замечания касаются также базовых переменных, используемых в сравнениях или в любых других скалярных выражениях. В официальном документе стандарта [8.1] подробно объясняется, что означает для двух типов быть совместимыми.
8. Базовые переменные для столбцов SQL должны иметь те же имена.
9. Как уже упоминалось, каждое выражение SQL в принципе должно сопровождаться проверкой возвращаемого значения sqlstate. Оператор whenever предназначен для упрощения этого процесса и имеет следующий синтаксис:
ЕХЕС SQL WHENEVER condition action terminator
Здесь terminator— это завершающий символ, упомянутый в пункте 1, условие condition может принимать значения sqlerror (ошибка SQL) или not found (не найдено), а действие action — это либо оператор continue (продолжить), либо оператор go то (перейти к). Оператор whenever не является выполняемым; это просто директива для процессора языка SQL. Наличие в программе выражения “ whenever condition go то label” приведет к тому, что процессор вставит оператор "if condition go то label" после каждого встретившегося ему выполняемого оператора SQL, а вот по выражению "whenever condition continue" процессор не вставляет никаких операторов, следовательно, программист должен будет вставлять такие операторы вручную. Два условия определяются так:
NOT FOUND означает никаких данных не найдено
(SQLSTATE = 02000) SQLERROR означает возникла ошибка
(см. описание стандарта [8.1] для SQLSTATE)
Каждый оператор whenever, который процессор SQL встречает при последовательном сканировании текста программы (для определенного условия), отменяет предыдущий (для этого условия).
Итак, для предварительного обсуждения достаточно. Ниже мы сосредоточимся на операторах языка обработки данных. Как уже отмечалось, большинство из них можно использовать непосредственным образом (т.е. с незначительными изменениями в синтаксисе). Однако операции выборки имеют специальную трактовку. Проблема состоит в том, что такие операторы выбирают не просто одну, а много строк (в общем случае), в то время как базовые языки обычно не приспособлены для обработки выборки более одной строки за один раз. Следовательно, необходимо обеспечить некоторого рода мост между уровнем выборки “множества-за-один-раз” в SQL и уровнем выборки “строки-за-один-раз” в базовом языке; и таким мостом являются курсоры. Курсор— это новый вид объекта SQL, который применяется только во встроенном SQL (так как в интерактивном SQL в нем нет необходимости). Он представляет собой такой вид указателя, который может быть использован для перемещения по набору строк, указывая поочередно на каждую из них и таким образом обеспечивая возможность адресации к этим строкам — к одной за один раз. Однако отложим подробности обсуждение курсоров до одного из следующих разделов и рассмотрим сначала такие операторы, для которых курсоры не требуются.
Операции, не использующие курсоров
Операторы обработки данных, для которых не требуется использовать курсоры, следующие:
• "единичный select",
• INSERT,
• UPDATE (кроме формы CURRENT),
• DELETE (кроме формы СURRENT).
Ниже приводятся примеры для каждого из этих операторов.