- •ВВЕДЕНИЕ
- •Лабораторная работа №1
- •Лабораторная работа №3-4
- •Создание первичного ключа двумя способами
- •Создание суррогатных ключей с помощью последовательностей
- •Ввод данных
- •Создание связей
- •Создание индексов
- •Изменение структуры таблиц, контрольные ограничения
- •Ограничения на модификацию столбцов таблиц
- •Представления
- •Контрольные задания к л.р. № 3-4
- •Лабораторная работа №5-6
- •Логика приложения
- •Обработка файлов PL/SQL
- •Хранимые процедуры
- •Хранимая процедура Customer_Insert
- •Листинг 1. Процедура CustomerInsert
- •Хранимая процедура NewCustomerWithTransaction
- •Задания к лабораторной работе.
- •Триггеры
- •Пример предваряющего триггера
- •Пример завершающего триггера
- •Обработка исключений
- •Словарь данных
- •Дополнительный материал для самостоятельного изучения
- •Управление параллельной обработкой
- •Уровень изоляции «завершенное чтение»
- •Уровень изоляции «сериализуемость»
- •Уровень изоляции «только чтение»
- •Дополнительные замечания о блокировках
- •Oracle и безопасность
исключения. Хранимые процедуры могут вызываться удаленно. Мы рассмотрим два примера хранимых процедур.
Хранимая процедура Customer_Insert
Галерее View Ridge требуется возможность добавлять в базу данных новых клиентов и записывать информацию о художниках, работами которых клиенты интересуются. В частности, нужно записывать имя и телефоны клиента, а также ассоциировать его со всеми художниками выбранной национальности.
В листинге 1 показана хранимая процедура, выполняющая эту задачу. Процедура, которая называется Customer_Insert, принимает четыре параметра: newname (имя нового клиента), newareacode (код региона), newphone (телефон) и artistnationality (национальность художника). Ключевое слово IN указывает на то, что вес эти параметры являются входными. Выходные параметры (которых у этой процедуры нет) обозначаются ключевым словом OUT, а параметры, играющие роль и входных, и выходных, — сочетанием IN OUT. Обратите внимание, что для параметра указывается тип данных, но не длина. Oracle определит длину из контекста.
Листинг 1. Процедура CustomerInsert
CREATE OR REPLACE PROCEDURE CustomerInsert
( |
|
|
newname |
IN |
char, |
newareacode |
|
IN char, |
newphone |
IN |
char, |
artistnationality |
IN char |
|
) |
|
|
AS
rowcount integer(4);
CURSOR artistcursor IS
SELECT ArtistID
FROM ARTIST
WHERE Nationality=artistnationality
33
BEGIN |
|
SELECT |
Count (*) INTO rowcount |
FROM |
CUSTOMER |
WHERE |
Name=newnaine AND Area_Code5newareacode AND |
Phone_Number = newphone;
IF rowcount > 0 THEN BEGIN
DBMS_OUTPUT.PUT_LINE ('Клиент уже есть в базе данных - никаких действий не предпринято');
RETURN;
END; END IF;
INSERT INTO CUSTOMER
(CustomerID, Name, Area_Code, Phone_Number) VALUES
(CustID.NextVal, newname, newareacode, newphone);
FOR artist IN artistcursor LOOP
INSERT INTO CUSTOMER_ARTIST_INT (CustomerID, ArttstID)
VALUES
(CustID.CurrVal, artist.ArtistID); END LOOP;
DBMS_OUTPUT.PUT_LINE (‘ Новый клиент успешно добавлен в базу '); END;
/
Выполнить процедуру!
34
Раздел объявления переменных следует за ключевым словом AS. Оператор SELECT определяет переменную-курсор (cursor variable) с именем artistcursor. Этот курсор выделяет из таблицы ARTIST для обработки строки всех художников заданной национальности.
В первом разделе процедуры выполняется проверка, нет ли уже в базе информации о данном клиенте. В этом случае никакие действия не предпринимаются, а пользователю с помощью пакета Oracle DBMS_OUTPUT выводится соответствующее сообщение.
!!! Обратите внимание, что пользователь получит это сообщение только в том случае, если процедура будет вызвана из SQL Plus. В случае вызова процедуры иным путем, например с помощью браузера через Интернет, пользователь не увидит этого сообщения. Чтобы сообщить пользователю об ошибке, разработчик должен воспользоваться выходным параметром или сгенерировать исключение. Синтаксис для вывода строки и значения переменной, хотя это не показано в листинге 1, имеет вид
DBMS_OUTPUT.PUT_ LINЕ('строка'//переменная).
Кроме того, чтобы такие сообщения стали видимыми, следует выполнить команду
Set serveroutput on:
Если при работе в SQL Plus Вы не видите сообщений, выводимых вашими процедурами, скорее всего, вы не выполнили этот оператор.
Оставшаяся часть процедуры в листинге 1 вставляет данные о новом клиенте и затем перебирает всех художников выбранной национальности. Обратите внимание на использование специальной конструкции PL/SQL
FOR artist IN artistcursor.
Эта конструкция выполняет несколько задач. Прежде всего, она открывает курсор и считывает первую строку. Затем она последовательно обрабатывает все строки под курсором и по окончании обработки передает управление следующему оператору после FOR.
Заметьте также, что обращение к столбцу ArtistID текущей строки происходит с использованием синтаксиса artist.ArtistID, где artist — это имя
переменной цикла FOR, а не курсора.
После того как процедура написана, ее необходимо скомпилировать и сохранить в базе данных. Наберите текст процедуры в редакторе и сохраните
35
его под именем, скажем, SP_CI.sql. Если в последней строке файла вы поместите косую черту, то процедура будет скомпилирована и сохранена в базе данных автоматически после ввода команды
Start SP_CI
Если вы что-то ввели неправильно, у нас могут возникнуть ошибки компиляции. К сожалению, SQL Plus не покажет вам эти ошибки автоматически, а выдаст сообщение: «Warning: Procedure created with compilation errors» («Предупреждение: при компиляции процедуры обнаружены ошибки»).
Чтобы увидеть ошибки, введите команду
Show errors;
Если синтаксических ошибок не было, вы получите сообщение «Procedure created» («Процедура создана»). Теперь вы можете вызывать эту процедуру с помощью команд EXECUTE или ЕХЕС:
Exec Customer_Insert(‘Selma Warning', '206', '555-0099', 'US');
Если возникнут ошибки на этапе выполнения процедуры, номера строк в отчете об ошибках не будут совпадать с номерами строк, которые вы можете видеть в своем текстовом редакторе. Можно настроить SQL Plus так, чтобы выводимые номера строк соответствовали вашим (но мы это не рассматриваем!) Главное, имейте в виду, что номера строк могут не совпадать.
Хранимая процедура NewCustomerWithTransaction
Приведем листинг 2 хранимой процедуры, реализующей процесс создания представления
Листинг 2 Процедура NewCustomerWithTransaction
CREATE OR REPLACE PROCEDURE NewCustoirierWithTransaction
(
newnawe IN char, newareacode IN char, newphone IN char, artistname IN char,
36
worktit1e IN char, workcopy IN char, price IN number
)
AS
rowcount integer (2) :
tid |
int: |
aid |
int; |
|
CURSOR transcursor IS |
|
SELECT TransactionID, ARTIST.ArtlstID |
|
FROM ARTIST, WORK, TRANSACTION |
|
WHERE Name=artistname AND Tit1e=worktit1e AND |
|
Copy=workcopy AND |
TRANSACTION.CuStomerID IS NULL AND
ARTIST.ArtlstID = WORK.ArtistID AND
WORK.WorkID = TRANSACTION.WorkID;
BEGIN
/* Клиент уже есть в базе данных? */
SELECT |
Count (*) INTO rowcount |
FROM |
CUSTOMER |
WHERE |
Name=newname AND Area_Code=newareacode AND |
|
Phone_Number = newphone |
IF rowcount > 0 THEN BEGIN
DBMS_OUTPUT.PUT_LINE ('Customer Already Exists – No Action Taken');
RETURN;
END; END IF;
/* Клиента нет в базе данных, добавляем нового клиента */
INSERT INTO CUSTOMER
37
(CustomerID, Name, Area_Code, Phone_Number) VALUES
(CustID.NextVal, newname, newareacode, newphone);
/* Ищем одну и только одну свободную строку в таблице TRANSACTION. */ rowcount :5 0;
FOR trans In transcursor LOOP
tid := trans.TransactionID; aid := trans.ArtistID; rowcount := rowcount + 1;
END LOOP; IF rowcount > 1 Then
BEGIN
/* Слишком много свободных строк -- выдаем сообщение об ошибке, отменяем изменения и выходим из процедуры */
ROLLBACK;
DBMS_OUTPUT.PUT_LINE (‘ Неверные данные в таблицах ARTIST/WORK/TRANSACTION -- никаких действий не предпринято ‘);
RETURN;
END; END IF;
IF rowcount 5 0 Then BEGIN
/* Нет ни одной свободной строки – выдаем сообщение об ошибке, отменяем изменения и выходим из процедуры */
ROLLBACK;
DBMS_OUTPUT.PUT_LINE (' Ни одной свободной строки в таблице TRANSACTION -- никаких действий не предпринято ');
RETURN;
END; END IF:
38
/* Есть ровно одна строка -- используем ее. Идентификатор транзакции. полученный из transcursor, находится в переменной ttd */
DBMS_OUTPUT.PUT_LINE (t1d); UPDATE TRANSACTION
SET CustomerID = CustID.Currvat , Salesprtce = price, PurchaseDate = SysDate WHERE TransactionID – tid;
DBMS OUTPUT.PUT_LINE (‘ Клиент добавлен в базу данных, данные транзакций обновлены ');
/* Теперь регистрируем интерес данного клиента к данному художнику */
/* Используем идентификатор художника, находящийся в переменной aid. и текущее значение последовательности
CurrVal */
INSERT INTO CUSTOMER_ARTIST INT (ArtistID, CustomerID)
VALUES (aid, CuitID.CurrVal);
END;
/
Логика этой процедуры, носящей имя NewCustomerWithTransaction, такова. Сначала создаются данные нового клиента, и в таблице TRANSACTION, где регистрируются купленные произведения, ведется поиск строк, и которых столбец CustomerID имеет пустое значение. Этот поиск ведется по соединению таблиц ARTIST, WORK и TRANSACTION, так как имя художника (Name) хранится в таблице ARTIST, а название произведения (Title) и номер копии (Copy) хранятся в таблице WORK. Если найдена ровно одна такая строка, в ней обновляются столбцы CustomerID (идентификатор клиента), SalesPrice (цена продажи) и PurchaseDate (дата приобретения). Затем добавляется запись в таблицу пересечения, чтобы зарегистрировать интерес клиента к данному художнику. В противном случае, если число найденных строк больше или меньше 1, никаких изменений в базе данных не производится.
Параметры процедуры NewCustomerWithTransaction содержат информацию о клиенте и приобретенном произведении. В процедуре объявлены несколько переменных и курсор. Курсор определен на соединении таблиц ARTIST,
39
WORK и TRANSACTION, Он выделяет столбцы TransactionID и ARTIST. ArtistID из строк, содержащих данные об искомом художнике и произведении и имеющих нулевое значение столбца CustomerID.
Прежде всего процедура выполняет проверку, нет ли уже в базе данных информации о данном клиенте. Если нет, данные нового клиента добавляются в базу. В PL/SQL нет оператора BEGIN TRANSACTION1 — первое действие с базой данных автоматически начинает транзакцию. Здесь новая транзакция начнется при добавлении данных о покупателе.
После того как данные о новом покупателе добавлены в базу, обрабатывается курсор TransCursor. Переменная rowcount используется для подсчета строк, в переменную tid записывается значение TransactionID, а в переменную aid — значение ArtistID. Заметьте, что оператор присваивания в Oracle выглядит так: «:=». Например, tid := trans.transactionID обозначает, что в переменную tid записывается значение trans.TransactionID.
В соответствии с логикой процедуры, если найдена одна и только одна строка, удовлетворяющая критериям, то в переменных tid и aid будут содержаться значения, нужные нам для успешного завершения транзакции. Если же таких строк не найдено или найдено более одной, то транзакция будет прервана, и ни tid, ни aid не будут использованы.
Для подсчета строк мы могли бы использовать выражение Count(*), и затем, если Count(*) = 1, запустить другой оператор, который бы получал нужные нам значения aid и tid. При той логике, которая реализована и листинге 2, необходимость во втором SQL-операторе отпадает.
Если число строк RowCount больше единицы или равно нулю, то выдается сообщение об ошибке и выполняется откат транзакции, отменяющий вставку данных в таблицу CUSTOMER. Если RowCount равно 1, обновляется соответствующая строка в таблице TRANSACTION. Обратите внимание на использование функции SysDate, с помощью которой записывается текущая дата. Наконец, в таблицу пересечения добавляется строка с информацией о покупателе и авторе купленного произведения (aid).
Эта хранимая процедура может быть вызвана командой наподобие
Exec NewCustorTierWithTransact1on( 'Susan Wu', '206', '555-1000', 'Miro', 'Mi Vida', 79/122', '65000');
Следующие два оператора отображают данные после обновления:
40