Модификация представления с помощью триггеров. Oracle позволяет «перехватить» попытку модификации представления, используя instead-of триггер. Пример. Likes(drinker, beer) Sells(bar, beer, price) Frequents(drinker, bar) CREATE VIEW Synergy AS SELECT Likes.drinker, Likes.beer,Sells.bar FROM Likes, Sells, Frequents WHERE Likes.drinker = Frequents.drinker AND Likes.beer = Sells.beer AND Sells.bar = Frequents.bar; 1 CREATE TRIGGER ViewTrig INSTEAD OF INSERT ON Synergy FOR EACH ROW BEGIN INSERT INTO Likes VALUES(:new.drinker, :new.beer); INSERT INTO Sells(bar, beer) VALUES(:new.bar, :new.beer); INSERT INTO Frequents VALUES(:new.drinker, :new.bar); END; . RUN SQL Triggers Рекомендуется прочитать раздел 7.4.3 книги. Некоторые отличия, включая 1. Ограничения Oracle относительно модификации отношения вызвавшего триггер или отношения связанного с ним внешним ключом отсутствует в SQL (однако Oracle –реальная СУБД, SQL – стандарт на бумаге). 2. Действие в SQL - список (ограниченный) SQL операторов, а не PL/SQL оператор, как в Oracle. 3. Имеются некоторые синтаксические отличия Пример 1. MovieExec (name, address, cert#, netWorth) CREATE TRIGGER NetWorthTrigger AFTER UPDATE of netWorth ON MovieExec REFERENCING OLD ROW AS OldTuple, NEW ROW AS NewTuple FOR EACH ROW WHEN (OldTuple.netWorth > NewTuple.netWorth ) UPDATE MovieExec SET netWorth = OldTuple.netWorth WHERE cert# = NewTuple.cert# FOR EACH ROW – триггер уровня кортежа, FOR EACH STATEMENT – триггер уровня оператора, UPDATE, INSERT, DELETE – возможные события, BEGIN … END – если действие состоит из нескольких SQL операторов. Пример 2. CREATE TRIGGER AvgNetWorthTrigger AFTER UPDATE OF netWorth ON movieExec REFERENCING OLD TABLE AS OldStuff, NEW TABLE AS NewStuff FOR EACH STATEMENT WHEN ( 500000 > (SELECT AVG(netWorth) FROM Movie) ) BEGIN DELETE FROM MovieExec WHERE (name, address, cert#, netWorth) IN NewStuff; INSERT INTO MovieExec (SELECT * FROM OldStuff); END; PL/SQL Oracle версия хранимых процедур SQL( PSM - Persistent, Stored Modules). Используется через sqlplus. Компромисс между полным процедурным языком программирования и SQL операторами – операторами высокого уровня, но ограниченными с точки зрения управления программой. Имеет локальные переменные, циклы, процедуры, возможность работы и проверки отдельного кортежа отношения. Общий вид: DECLARE Объявления (описания) переменных BEGIN Выполнимые операторы END; . RUN; секция DECLARE не обязательна. . и RUN (или слеш вместо RUN) необходимо, чтобы закончить программу и выполнить ее. Простейшая форма: последовательность изменений Likes(drinker, beer) BEGIN INSERT INTO Likes VALUES('Sally', 'Bud'); DELETE FROM Likes WHERE drinker = 'Fred' AND beer = 'Miller'; END; . RUN; Procedures. Хранимые объекты базы данных, использующие PL/SQL утверждения в своем теле. Объявление процедур. CREATE OR REPLACE PROCEDURE < имя > ( < список_аргументов > ) AS < объявления > BEGIN < PL/SQL операторы > END; . RUN; Список_аргументов состоит из троек имя-режим-тип. Режим: IN, OUT, или IN OUT для только-чтения, толькозаписи или чтения-записи соответственно Тип: стандартный SQL тип + общие типы, такие как NUMBER = любое целое или вещественное значение. Поскольку типы в процедуре должны соответствовать типам в схеме базы данных, рекомендуется использовать выражения вида <отношение>.<атрибут>%TYPE чтобы это обеспечить. Пример. Процедура принимает в качестве параметров beer и price и добавляет в меню бара Joe's. Sells(bar, beer, price) CREATE PROCEDURE joeMenu( b IN Sells.beer%TYPE, p IN Sells.price%TYPE ) AS BEGIN INSERT INTO Sells VALUES('Joe''s Bar', b, p); END; . RUN; Замечание: RUN только компилирует и запоминает процедуру в базу данных, но не выполняет ее. Вызов процедуры. Вызов процедуры может использоваться в теле PL/SQL оператора. Пример. BEGIN joeMenu('Bud', 2.50); joeMenu('MooseDrool', 5.00); END; . RUN; Присваивание. Значение объявленной переменной присваивается при помощи оператора :=. Ветвление. IF < condition > THEN < statement(s) > ELSE < statement(s) > END IF; Но во вложенных условных операторах используются ELSIF вместо ELSE IF. Циклы. LOOP ... EXIT WHEN < условие > … END LOOP; Запросы в PL/SQL. 1. Однострочный SELECT (результатом которого является ровно один кортеж) позволяет записать результат запроса в переменную (переменные). 2. Курсор позволяет извлекать результаты многострочного запроса по одному кортежу. Однострочный SELECT. SELECT-FROM-WHERE в PL/SQL должен иметь опцию INTO со списком переменных, в которые будут записаны компоненты кортежа. Если результат запроса содержит более одного кортежа, возникает ошибка. Для обработки результата в этом случае необходим курсор. Пример. Найти цену Joe берет за Bud (на самом деле она никак в этом примере не используется). Sells(bar, beer, price) DECLARE p Sells.price%TYPE; BEGIN SELECT price INTO p FROM Sells WHERE bar = 'Joe''s Bar' AND beer = 'Bud'; END; . RUN Курсоры. Объявляются при помощи CURSOR < имя_курсора > IS SELECT-FROM-WHERE запрос Курсор получает каждый кортеж из результата запроса по очереди, используя оператор FETCH в цикле. FETCH < имя_курсора > INTO <список_переменных>; Окончание цикла происходит при обнаружении конца результата: EXIT WHEN < имя_курсора > %NOTFOUND; Условие становится истинным, когда просмотрены (извлечены) все кортежи результата. Курсор перед использованием должен быть открыт при помощи оператора OPEN, а после использования – закрыт при помощи CLOSE. Пример. Процедура проверяет меню бара Joe's и поднимает цену на $1.00, если она меньше, чем $3.00. Sells(bar, beer, price) Этот простой алгоритм может быть реализован при помощи единственного утверждения UPDATE, но в более сложных случаях без курсора не обойтись. CREATE PROCEDURE joeChange() AS theBeer Sells.beer%TYPE; thePrice Sells.price%TYPE; CURSOR c IS SELECT beer, price FROM Sells WHERE bar = 'Joe''s bar'; BEGIN OPEN c; LOOP FETCH c INTO theBeer, thePrice; EXIT WHEN c%NOTFOUND; IF thePrice < 3.00 THEN UDPATE Sells SET price = thePrice + 1.00 WHERE bar = 'Joe''s Bar' AND beer = theBeer; END IF; END LOOP; CLOSE c; END; . RUN Строчный тип. Объекты, состоящие из кортежей, имеют тип %ROWTYPE. Можно объявить переменную-кортеж, имеющую строчный тип и обращаться к компонентам кортежа при помощи . (оператора «точки») и имени атрибута. Удобно, когда кортеж имеет много атрибутов. Пример. Та же процедура с использованием переменнойкортежа. CREATE PROCEDURE joeGouge() AS CURSOR c IS SELECT beer, price FROM Sells WHERE bar = 'Joe''s bar'; bp c%ROWTYPE; BEGIN OPEN c; LOOP FETCH c INTO bp; EXIT WHEN c%NOTFOUND; IF bp.price < 3.00 THEN UDPATE Sells SET price = bp.price + 1.00 WHERE bar = 'Joe''s Bar' AND beer = bp.beer; END IF; END LOOP; CLOSE c; END; . RUN