|
Хранимые процедуры
Хранимые процедуры
Хранимые процедуры (stored procedures) находятся на сервере баз данных, а не на компьютере пользователя, как это было до появления SQL/PSM. Хранимая процедура должна быть определена, после чего ее можно вызвать с помощью команды CALL. Хранение процедуры на сервере уменьшает сетевой обмен и повышает производительность. Команда CALL является единственным сообщением, передаваемым от пользователя к серверу. Ниже приведен пример создания процедуры.
EXEC SQL
CREATE PROCEDURE MatchScore
(IN white CHAR (20),
IN black CHAR (20),
IN result CHAR (3),
OUT winner CHAR (5) )
BEGIN ATOMIC
CASE result
WHEN '1-0' THEN
SET winner='white' ;
WHEN '0-1 ' THEN
SET winner='black' ;
ELSE
SET winner='draw' ;
END CASE END ;
После создания хранимой процедуры ее можно вызвать с помощью команды CALL, например:
CALL MatchScore ('Kasparov', 'Karpov', '1-0', winner);
Три первых аргумента являются входными параметрами, которые передаются процедуре MatchScore. Четвертый аргумент является выходным параметром, который процедура Match-Score использует для возврата своих результатов в вызывающую программу. В приведенном примере она возвращает значение 'white'.
IF...THEN...ELSE...END IF
IF...THEN...ELSE...END IF
Основной управляющей структурой является IF...THEN...ELSE...END IF. Если условие IF истинно, выполняются команды предложения THEN. В противном случае выполняются команды предложения ELSE. Например:
IF
vfname = 'Brandon'
THEN
UPDATE students
SET Fname = 'Brandon'
WHERE StudentID = 314159 ;
ELSE
DELETE FROM students
WHERE StudentID = 314159 ;
END IF
В приведенном примере, если переменная vfname содержит значение 'Brandon', в списке студентов произойдет обновление записи с идентификатором 314159— в поле Fname будет помещено значение 'Brandon1. Если же переменная содержит какое-либо другое значение, отличное от Brandon', запись с идентификатором 314159 будет удалена из таблицы Students.
Выражение IF...THEN...ELSE...END IF больше всего подходит, если в зависимости от выполнения некоторого условия существуют два варианта дальнейшего поведения программы. Однако очень часто приходится иметь дело с большим количеством вариантов. В таких случаях следует использовать оператор CASE.
ITERATE
ITERATE
Оператор ITERATE позволяет изменять последовательность выполнения команд в циклах SQL. Циклы SQL представлены операторами LOOP, WHILE, REPEAT и FOR. Если условие в операторах циклов является истинным или не задано, тут же оператор ITERATE начинает следующую итерацию цикла. Если же условие итерации является ложным или неопределенным, цикл завершается и выполняются следующие за ним команды:
AsteroidPreload4:
SET vcount = 0 ;
WHILE
vcount < 1000 DO
SET vcount=vcount+l ;
INSERT INTO asteroid (AsteroidID)
VALUES (vcount) ;
ITERATE AsteroidPreload4 ;
SET vpreload = 'DONE' ;
END WHILE AsteroidPreload4
Оператор ITERATE все время передает управление в начало цикла WHILE, но до тех пор, пока переменная vcount не будет равна 9999. В следующей итерации переменная vcount увеличится до 10000, выполнится оператор INSERT, после чего оператор ITERATE прекратит итерации, переменная vpreload получит значение 'DONE', а выполнение перейдет к команде, следующей за циклом.
Курсоры
Курсоры
В составных командах можно использовать курсоры. Как уже упоминалось в главе 18, курсоры используются для построчной обработки данных. В составной команде можно объявить курсор, использовать его, а затем забыть о нем, так как курсор уничтожается сразу после завершения выполнения составной команды. Ниже приведен пример использования курсора.
BEGIN
DECLARE ipocandidate CHARACTER (30) ;
DECLARE cursorl CURSOR FOR
SELECT company
FROM biotech ;
OPEN cursor1 ;
FETCH cursor1 INTO ipocandidate ;
CLOSE cursor1;
END ;
LEAVE
LEAVE
Оператор LEAVE (покинуть) действует согласно названию. Как только выполнение программы доходит до оператора LEAVE с меткой, он передает управление команде, расположенной сразу после помеченной. Например:
AsteroidPreload:
SER vcount = 0 ;
LOOP
SET vcount = vcount+1 ;
IF vcount > 1000
THEN
LEAVE AsteroidPreload;
END IF ;
INSERT INTO asteroid (AsteroidID)
VALUES (vcount) ;
END LOOP AsteroidPreload
Приведенный выше код создает 1000 последовательно пронумерованных записей в таблице астероидов и затем завершает цикл.
LOOP...END LOOP
LOOP...END LOOP
Структура LOOP позволяет многократно выполнять некоторую последовательность команд SQL. После выполнения последней команды SQL, находящейся внутри структуры LOOP...ENDLOOP, цикл передает управление первой команде цикла, и все команды цикла выполняются еще раз. Синтаксис структуры LOOP...ENDLOOP имеет следующий вид:
LOOP
SET vcount=vcount+1 ;
INSERT INTO asteroid (AsteroidID)
VALUES (vcount) ;
END LOOP
Приведенный фрагмент кода готовит заготовку для таблицы астероидов, заполняя ее записями пока без данных, но с уникальными идентификаторами. По мере изучения астероидов эту таблицу можно будет заполнить и другими данными.
Необходимо отметить, что данный фрагмент кода имеет один существенный недостаток. Созданный им цикл является бесконечным. В нем нет кода окончания, следовательно, добавление в таблицу строк будет продолжаться до тех пор, пока СУБД будет располагать местом на диске. Затем система в лучшем случае сгенерирует исключение, а в худшем — просто аварийно завершит работу.
Для практического использования оператора LOOP необходимо иметь возможность выхода из цикла без возникновения исключения. Для этого имеется оператор LEAVE.
Необрабатываемые состояния
Необрабатываемые состояния
Предположим, в предыдущем примере возникнет состояние, при котором значение SQLSTATE будет отличным от '23000'. Ваш обработчик не может обработать подобное состояние. Так как в текущей процедуре такое состояние не предусмотрено, выполняется команда RESIGNAL. После этого состояние может быть обработано на более высоком уровне. Если же и на этом уровне состояние нельзя обработать, она передается на еще более высокий уровень и т.д., пока состояние не будет обработано или пока не будет сгенерирована ошибка в основном приложении. Основная идея вышесказанного заключается в следующем: если во время выполнения команды SQL возможно появление исключений, необходимо написать обработчики всех возможных исключений. Это позволит намного упростить отладку.
Объявление обработчиков состояний
Объявление обработчиков состояний
Обработчик состояния можно поместить в составную команду. При создании обработчика вначале следует объявить состояние, которое обработчик должен обрабатывать. Объявленное состояние может быть исключением или же некоторым другим состоянием. В табл. 19.2 представлены возможные состояния, а также кратко описаны причины возникновения состояния каждого вида.
Обработка состояний
Обработка состояний
Программу можно написать таким образом, чтобы значение параметра SQLSTATE контролировалось после выполнения каждой команды SQL. Итак, используем приобретенные знания на практике.
При получении сообщения кода класса '00' вряд ли необходимо предпринимать дополнительные действия. Продолжайте делать то, что вы планировали.
При получении сообщения кода класса '01' или '02' разработчик должен решить, как программа должна отреагировать. Если это допустимые состояния программы, выполнение должно продолжиться по плану. В том случае, если такие сообщения не должны появляться, необходимо перейти к выполнению специальной процедуры, обрабатывающей подобные ситуации.
Получение какого-либо другого класса кода указывает на то, что в программе имеются ошибки. В этом случае следует перейти к процедуре обработки исключения. Конкретная процедура, на которую должно перейти выполнение программы, зависит от содержимого параметра SQLSTATE. Если существует возможность множества различных состояний, для каждого следует использовать собственную процедуру обработки, так как различные состояния часто должны обрабатываться по-разному. Некоторые ошибки несущественны или поправимы. Другие являются фатальными и приводят к аварийному завершению приложения.
Оператор CASE с поиском
Оператор CASE с поиском
Оператор CASE с поиском аналогичен простому оператору CASE, за исключением того, что используется несколько условных выражений, а не одно. Например:
CASE
WHEN vmajor
IN ('Computer Science', 'Electrical Engineering')
THEN INSERT INTO geeks (StudentID, Fname, Lname)
VALUES(:sid, :sfname, :slname) ;
WHEN vclub
IN ('Amateur Radio', 'Rocket1,'Computer')
THEN INSERT INTO geeks (StudentID, Fname, Lname)
VALUES(:sid, :sfname, :slname) ;
ELSE
INSERT INTO poets (StudentID, Fname, Lname)
VALUES(:sid, :sfname, :slname) ;
END CASE
Чтобы избежать возможности возникновения исключения, следует поместить всех студентов, которые не являются "физиками" (geeks), в таблицу poets ("лирики")- Конечно, не все "не физики" являются "лириками" — ведь есть же еще и "футболисты", не говоря уже о "химиках". Это не меняет сути дела, так как в оператор CASE всегда можно добавить еще несколько предложений WHEN.
Операторы управления ветвлением
Операторы управления ветвлением
Основным недостатком стандарта SQL-86, не позволяющим считать SQL полноценным процедурным языком, являлось отсутствие управляющих структур. До появления SQL/PSM строгую последовательность выполнения команд нельзя было нарушить без использования базового языка, такого как С или Basic. SQL/PSM получил в свое распоряжение аналогичные управляющие структуры, позволяя тем самым решать многие задачи без привлечения других языков программирования.
Переменные
Переменные
Все высокоуровневые языки программирования, такие как С или Basic, позволяют использовать переменные. До появления SQL/PSM переменные в SQL использовать было нельзя. Переменные являются символическими именами значений определенного типа. В составных командах можно объявить переменную и определить ее значение. В ходе выполнения составной команды переменная может использоваться. После завершения выполнения составной команды все переменные, объявленные в ней, уничтожаются. Таким образом, переменные в SQL являются локальными переменными составной команды, в которой они объявлены. Рассмотрим следующий пример:
BEGIN
DECLARE prezpay NUMERIC ;
SELECT salary
INTO prezpay
FROM EMPLOYEE
WHERE jobtitle= 'president' ;
END ;
Полномочия
Полномочия
В главе 13 рассматривались различные полномочия, предоставляемые пользователям. Владелец базы данных может предоставить другим пользователям следующие полномочия.
На удаление записи из таблицы.
На вставку записи в таблицу.
На обновления записи в таблице.
На создание ссылок на поля таблицы.
На использование домена.
SQL/PSM добавляет к уже существующим еще один вид полномочий — полномочия на выполнение. Ниже приведены два примера.
GRANT EXECUTE on MatchScore to TournamentDirector ;
GRANT EXECUTE on PurchaseHistory to SalesManager ;
Эти команды предоставляют возможность руководителю шахматного турнира вызывать процедуру MatchScore, а менеджеру по продажам компании — вызывать функцию Purchase-History. Пользователи, которые не имеют полномочий на выполнение подпрограммы, не могут ее использовать.
Присваивание
Присваивание
С появлением SQL/PSM SQL наконец-то получил возможность, которую всегда имели даже самые примитивные процедурные языки, — возможность присваивания значения переменной. Присваивание выглядит так:
SET переменная = значение ;
где переменная — имя переменной, а значение — выражение. Ниже приведено несколько примеров присваивания.
SET vfname = 'Brandon' ;
SET varea = 3.1416 * :radius * :radius ;
SET vhiggsmass = NULL ;
Простой оператор CASE
Простой оператор CASE
Простой оператор CASE вычисляет одно выражение. В зависимости от его значения выполнение программы происходит одним из нескольких возможных путей. Например:
CASE vinajor
WHEN 'Computer Science'
THEN INSERT INTO geeks (StudentID, Fname, Lname)
VALUES(:sid, :sfname, :slname) ;
WHEN 'Sports Medicine'
THEN INSERT INTO jocks (StudentID, Fname, Lname)
VALUES(:sid, rsfname, :slname) ;
ELSE INSERT INTO undeclared (StudentID, Fname, Lname)
VALUES(:sid, :sfname, :slname) ;
END CASE
Команды предложения ELSE выполняются, если vmajor неТгопадает ни в одну из категорий, заданных предложениями THEN.
REPEAT...UNTIL..END REPEAT
REPEAT...UNTIL..END REPEAT
Цикл REPEAT очень похож на цикл WHILE. Различие между ними заключается в том, что условие проверяется после выполнения команд цикла. Например:
AsteroidPreload3 :
SET vcount = 0 ;
REPEAT
SET vcount = vcount+1 ;
INSERT INTO asteroid (AsteroidID)
VALUES (vcount) ;
UNTIL vcount = 1000
END REPEAT AsteroidPreload3
Совет 1
Совет 1
Хотя во всех трех приведенных выше примерах одна и та же операция с одинаковой легкостью выполнялась тремя различными методами (с помощью операторов LOOP, WHILE и REPEAT), очень часто встречаются ситуации, когда один из методов имеет явные преимущества перед другими. Поэтому лучше хорошо знать все три метода и в конкретных обстоятельствах выбрать наиболее подходящий.
Составные команды
Составные команды
В этой книге SQL рассматривается как непроцедурный язык, который применяется в основном для обработки наборов данных, а не отдельных записей. В то же время в этой главе вы узнаете, что это положение постепенно меняется. SQL становится процедурным языком, в основных чертах все еще оставаясь средством для обработки наборов данных. Версия SQL, определяемая стандартом SQL-92, не соответствовала процедурной модели, в которой команды выполняются одна за другой в определенной последовательности. Команды SQL были одиночными и, как правило, встроенными в код программы C++ или Visual Basic. Фактически с помощью ранних версий SQL пользователи не могли создать запрос или выполнить некоторые другие операции с помощью последовательности операторов SQL. Выполнение же последовательности команд SQL вызывало значительное снижение производительности. Каждая выполняющаяся команда SQL должна послать сообщение с места работы пользователя на сервер баз данных, а тот должен отослать ответ в обратном направлении. Это увеличивает сетевой обмен и тем самым приводит к дополнительной нагрузке на сеть.
SQL: 1999 и SQL:2003 предоставляют возможность создания составных команд, образованных отдельными командами SQL, выполняемыми в один прием. Использование составных команд позволяет снизить загрузку сети, поскольку все отдельные команды, образующие составную, вместе пересылаются на сервер, возвращающий обратно один ответ.
Все команды, включенные в составную команду, должны быть расположены между ключевыми словами BEGIN и END. Например, чтобы вставить данные во множество связанных таблиц, следует использовать следующий синтаксис:
void main {
EXEC SQL
BEGIN
INSERT INTO students (StudentID, Fname, Lname)
VALUES (:sid, :sfname, :sid) ;
INSERT INTO roster (ClassID, Class, StudentID)
VALUES (:cid, rename, :sid) ;
INSERT INTO receivable (StudentID, Class, Fee)
VALUES (:sid, :cname, :cfee) ;
END ;
/* Проверка значения SQLSTATE на код ошибки */
}
Приведенный выше небольшой фрагмент программы на языке С содержит встроенную составную команду SQL. Комментарии относятся к обработке ошибок. Если почему-то составная команда не выполнилась, в специальный параметр SQLSTATE будет помещен код ошибки. Понятно, что комментарий не может обработать ошибку. Я поместил его лишь с целью напомнить о том, что в реальной программе код обработки ошибок находится в этом месте.
Состояния
Состояния
Все знают, что означает, когда о человеке говорят, что он дошел до "кондиции". Его состояние наутро после этого может быть тяжелым, очень тяжелым, или же все обойдется. Так и в SQL. Результат выполнения команды SQL может быть успешным, сомнительным или неправильным. Каждый из таких результатов является "состоянием" (condition).
Каждый раз, когда выполняется команда SQL, сервер базы данных обновляет значение параметра статуса SQLSTATE. Он представляет собой поле из пяти символов. Значение SQLSTATE содержит информацию об успешном или неудачном завершении команды. В случае возникновения ошибки значение содержит ее код.
Первые два из пяти символов значения класса SQLSTATE содержат информацию о том, выполнены ли команды SQL успешно, непонятно как или неудачно. В табл. 19.1 приведены четыре возможных результата.
Значения класса sqlstate
Таблица 19.1. Значения класса sqlstate
Класс |
Описание |
00 |
Успешное завершение |
01 |
Предупреждение |
02 |
Не найден |
Другое |
Сгенерировано исключение |
Значение класса '00' означает, что предыдущая команда SQL выполнена успешно. В большинстве случаев это наиболее желаемый результат.
Значение класса '01' означает предупреждение. Предупреждение указывает на то, что в ходе выполнения команды SQL случилось что-то незапланированное. В данном случае СУБД не "знает", произошла ошибка или нет. Предупреждение обращает внимание разработчика на то, что требуется тщательно проверить команду SQL.
Значение класса '02' означает, что не получено никаких данных в результате выполнения команды SQL. Это может быть хорошим или плохим известием, в зависимости от обстоятельств. Иногда результат в виде пустой таблицы — как раз то, что требуется разработчику.
Любой код, отличный от значений '00', '0Г или '02', указывает на наличие ошибки в программе. Три последних символа параметра SQLSTATE содержат код ошибки. Два символа кода класса и три символа кода ошибки вместе составляют пять символов значения SQLSTATE.
Состояния, которые можно определить в обработчике
Таблица 19.2. Состояния, которые можно определить в обработчике
Состояние |
Описание |
SQLSTATE VALUE ' xxyyy ' |
Соответствующее значение sqlstate |
SQLEXCEPTION |
Класс sqlstate, отличный от '00', ' 01' или '02' |
SQLWARNING |
Класс ' 01' |
NOT FOUND |
Класс ' 02' |
Ниже приведен пример объявления состояния.
BEGIN
DECLARE constraint_violation CONDITION
FOR SQLSTATE VALUE '23 000' ;
END ;
Приведенный пример не является взятым из жизни, поскольку внутри структуры BEGIN...END должны располагаться и команда SQL, ответственная за возникновение особого состояния, и его обработчик.
Сложные команды, атомарность, курсоры, переменные
В этой главе...
Сложные команды, атомарность, курсоры, переменные и состояния
Управляющие структуры
Создание циклов
Использование хранимых процедур и функций
Предоставление полномочий на выполнение
Создание и использование хранимых модулей
В течение многих лет ведущие специалисты в области технологий баз данных работали над разработкой стандартов. После выхода очередного стандарта и начала его применения пользователями и программистами во всем мире сразу начинается процесс разработки следующего. Так было и в случае SQL-92. Семь лет разделяет выпуск SQL-92 и выход первого компонента SQL: 1999. Однако все эти годы продолжалась кипучая деятельность, в результате которой ANSI и ISO выпустили дополнение к SQL-92, названное SQL-92/PSM (Persistent Stored Modules — постоянно хранимые модули). Это дополнение послужило основой для одного из разделов стандарта SQL: 1999 с тем же названием. SQL/PSM как часть SQL:2003 определяет набор операторов, предоставляющих SQL возможности по созданию управляющих структур, обычных для наиболее мощных языков программирования. Благодаря этому стало возможным решать многие задачи только с использованием SQL, без привлечения других программных средств. Предыдущие версии SQL требовали непрерывного переключения между SQL и процедурным базовым языком.
Оператор ELSE является необязательным. Однако,
Внимание
Оператор ELSE является необязательным. Однако, если он отсутствует и выражение CASE не соответствует ни одному предложению THEN, SQL генерирует исключение.
WHILE...DO...END WHILE
WHILE...DO...END WHILE
Оператор WHILE предоставляет другой метод многократного выполнения последовательности команд SQL. Если условие оператора WHILE истинно, цикл продолжает выполняться. Если нет — выполнение цикла тут же прекращается. Например:
AsteroidPreload2 :
SET vcount = 0 ;
WHILE
vcount < 100 0 DO
SET vcount = vcount+1 ;
INSERT INTO asteroid (AsteroidID)
VALUES (vcount) ;
END WHILE AsteroidPreload2
Результат работы этого кода точно такой же, как в предыдущем случае. Это еще раз подтверждает то, что в SQL существует много методов решения поставленной задачи. Забота программиста — выбрать наиболее подходящий.