Объектные модели данных
10.3 Объектно-реляционная модель данных Oracle
Изучая объектную модель данных, мы уже заметили, что методы были написаны на основном для Cache не объектно-ориентированном языке ObjectScript. В объектно-реляционной модели данных, которая будет рассмотрена на примере СУБД Oracle, ситуация с реализацией методов та же. Только методы создаются на встроенном процедурном языке, который называется PL/SQL.
Мы его изучим в минимально возможном объёме, достаточном для написания несложных методов. Полностью можно освоить PL/SQL по многочисленным учебникам и имеющейся технической документации.
Обратите внимание, что отмеченная особенность реализации методов указывает на то, что любые объектно-ориентированные языки — это языки моделирования с уровнем выше, чем у чисто процедурных языков, на которых они построены.
При изучении этого раздела, учтите, что используемая нами версия Oracle XE, видимо, не предназначалась для работы с объектно-реляционной моделью. Не всё, что реализуется в основных версиях СУБД Oracle, в ней удаётся. Но и того, что имеется, достаточно для изучения основ объектно-реляционной модели данных. С другой стороны, в Oracle XE удобно работать с планами исполнения SQL-запросов, и, главное, используя систему APEX, входящую в состав этой версии Oracle, легко самостоятельно освоить быструю разработку приложений.
Если вы хотите изучить объектно-реляционные базы полнее, скачайте персональный вариант Oracle 11g и используйте многочисленные учебные материалы Oracle.
Инсталляция этой СУБД и дополнительные сведения о ней приведены на сайте книги.
10.3.1 Введение в процедурный язык PL/SQL
Здесь будет описан минимальный набор средств строго типизированного языка PL/SQL, который позволит реализовать методы в объектно-реляционной опции Oracle.
Сразу обратим внимание на то, что в Oracle любая инструкция SQL и PL/SQL заканчивается точкой с запятой.
Блоки
Программы PL/SQL структурируются анонимными (не имеющими имени) блоками. Структура такого блока:
[DECLARE объявления ] BEGIN выполняемые операторы [EXCEPTION обработка исключительных ситуаций ] END;
Блок обязательно заканчивается точкой с запятой.
Необязательный раздел обработки исключительных ситуаций мы рассматривать не будем. Конечно, вы понимаете, что реальные программы без секций исключительных ситуаций это просто генераторы неприятностей для пользователя. Но для целей предварительного знакомства можно временно считать, что исключительных ситуаций не бывает.
В секции объявления (DECLARE) определяются переменные и константы. Они всегда локальны. Это означает, что они видны внутри блока и во всех блоках, вложенных в блок с их определением, но невидимы вне блока.
Объявление переменных и констант можно выполнить так:
имя_переменной тип_данных [[NOT NULL] := выражение_по_умолчанию]; имя_переменной тип_данных CONSTANT := выражение;
Присваивание обозначается знаком :=.
Разберём несложный пример анонимного блока (листинг 10.11). Набрав текст нажимаем Run.
DECLARE х NUMBER(5,2); у NUMBER(5,2):=2; result NUMBER(5,2); BEGIN — Зто однострочный комментарий result := 12.02; x := result / y; DBMSOUTPDT.PUT_LINE(1 хранен 1 || x); END; Results: x равен 6,01 Statement processed. 0,00 secondsПример 10.11. Анонимный блок PL/SQL
Язык PL/SQL, в отличие от SQL, "молчаливый" язык. Вывод на экран в нём необходимо прописать явно. Процедура PUT_LINE из пакета DBMS_OUTPUT позволяет вывести на экран строку, сформированную как конкатенация (обозначенная знаком ||) из двух строк —текста 'x равен ' и строки, представляющей значение x. Числовой тип x здесь неявно приводится к текстовому типу. Теперь строка DBMS_OUTPUT.PUT_LINE('x равен ' || x); понятна.
Запомните, что анонимный блок может быть подставлен вместо любого оператора PL/SQL или SQL.
SQL внутри PL/SQL
Поскольку PL/SQL не персистентный язык, то работу с базами данных в нём выполняют инструкции SQL. Запросы SELECT теперь должны выдавать результат не на экран, а в подготовленные переменные или другие структуры.
Создадим таблицу и введём в неё одну строку:
CREATE TABLE qq(c1 CHAR(10), c2 INTEGER); INSERT INTO qq VALUES('QWERTY', 1);
Извлечём значение первого столбца с помощью запроса типа SELECT .. INTO:
DECLARE result char(10); BEGIN SELECT cl INTO result FROM qq WHERE c2=1; DBMS_OUTPUT.PUT_LINE(result); END;
Удаление слов INTO result вызовет появление ошибки.
Разветвления и циклы
Синтаксис команды разветвления в общем обычный:
IF условие THEN последовательность_операторов_1 ELSIF условие2 THEN последовательность_операторов_2 ELSE последовательность_операторов_3 END IF;
Обратите внимание на то, что вместо обычного ELSEIF почему-то пишется ELSIF. Циклы строятся на основе так называемого простого цикла:
[<<имя_цикла>>] LOOP последовательность_операторов_1 EXIT имя_цикла WHEN условие_выхода последовательность_операторов_2 END LOOP;
Без инструкций останова EXIT или EXIT WHEN он зацикливается. Фраза в двойных угловых скобках "имя_цикла" это метка имени цикла. Пример простого цикла с меткой, которая в нём не используется, приведён в листинге 10.12:
DECLARE v_i INTEGER := 1; BEGIN "1QQP_1" LOOP DBHSOUTPUT.PUTLINE{'В цикле v_i = ' || v_i) EXIT loop_l WHEN v_i > 2; v_i := v_i + 1; END LOOP; DBMS_OUTPUT.PUT_LINE('Вышли из цикла') Results: В цикле v_i = 1 В цикле v_i = 2 В цикле v_i = 3 Вышли из циклаПример 10.12. Простой цикл
Пример цикла WHILE с предусловием:
DECLARE i INTEGER:=1; BEGIN WHILE i < 6 LOOP DBMS_OUTPUT.PUT_LINE(i); i :=i +1; END LOOP; END;
Пример цикла FOR:
BEGIN FOR i IN 1 .. 5 LOOP DBMS_OUTPUT.PUT_LINE(i); END LOOP; END;
Обратите внимание на то, что переменная i не была объявлена явно, и цикл сам создал её с типом INTEGER. Слово REVERSE во фразе FOR i IN REVERSE 1 .. 5 LOOP задало бы обратное направление перебора значений.
Процедуры и функции
Хранимые процедуры и функции создаются на основе анонимного блока путём добавления в него четвёртой секции, содержащей спецификацию процедуры/функции. В неё входят имя и список формальных параметров. Особенность функции в том, что она возвращает значение, а процедура нет.
Упрощенный синтаксис инструкции создания процедуры:
CREATE [OR REPLACE] PROCEDURE имя_процедуры [(имя_параметра [IN | OUT | INOUT] тип [, ...])] IS | AS BEGIN тело_процедуры END имя_процедуры;
Упрощенный синтаксис создания функции:
CREATE [OR REPLACE] FUNCTION имя_функции [(имя_параметра [IN | OUT | INOUT] тип [, ...])] RETURN тип_возвращаемого значения IS | AS BEGIN тело_функции END имя_функции;
Тело функции или процедуры это последовательность операторов.
Слова OR REPLACE добавляют, если необходимо заменить существующую функцию с таким же наименованием.
Режим параметров указывается как IN — входной, OUT — выходной, или INOUT — входо-выходной. Входной параметр нельзя изменять в теле функции, а выходному обязательно должно быть присвоено значение.
Пример функции, вычисляющей площадь круга по его радиусу:
CREATE OR REPLACE FUNCTION circle_area (p_radius IN NUMBER) RETURN NUMBER AS v_pi NUMBER := 3.1415926; v_area NUMBER; BEGIN v_area := v_pi * POWER(p_radius, 2); RETURN v_area; END circle_area;
Слово RETURN в теле функции присутствует, по крайней мере, один раз и указывает имя переменной, которая будет возвращена.
Обратите внимание на то, что в именованиях переменных использован префикс "v_", в имени формального параметра префикс "p_". Подобные соглашения об именованиях очень удобны, особенно если они соблюдаются широким кругом разработчиков.
Вызовем функцию circle_area из анонимного блока:
BEGIN DBMS_OUTPUT.PUT_LINE(circle_area(2)); END;
Созданная функция может быть вызвана и из SQL, например,
SELECT circle_area(2) FROM dual;
Dual это такая необычная таблица в Oracle. В ней единственный столбец dummy. Если пользоваться средствами, разработанными фирмой Oracle, то кажется, что она всегда состоит из одной строки. Dual используют чтобы "сделать вид" что все данных выбираются только из таблиц. Например, системная дата может быть получена запросом:
SELECT sysdate FROM dual;