Создание обновляющих представлений
Creating Updatable Views, By Steve Bobrowski
Источник: http://www.oracle.com/oramag/oracle/01-mar/index.html?o21o8i.html
Стив Бобровский
Эта статья была
создана на основе учебного курса, разработанного в компании Animated Learning,
Inc./The Database Domain, которая проводит обучение, основанное на Web и CD,
администраторов и разработчиков БД Oracle. Более подробную информацию можно
получить по адресу: http://www.dbdomain.com
Обновляющие представления могут быть сложной проблемой. В этой статье предлагается помощь в виде практических упражнений, которые можно выполнить, используя любую версию или релиз Oracle8i.
Представление, которое предоставляет доступ к данным одной или нескольких таблиц, является основным схемным объектом в базе данных Oracle. Представления обычно используют для создания дополнительного уровня защиты табличных данных, а также для упрощения разработки приложений. Создание обновляющих представлений, то есть представлений, которые можно использовать для вставки, обновления и удаления строк в таблицы базы, иногда может стать сложной задачей. В этой статье подробно объясняется, как создавать различные типы обновляющих представлений, используемых в приложениях.
Предполагается, что вы имеете базовое понятие о структурах реляционных баз данных, включая таблицы и представления, а также об основных SQL-командах INSERT, UPDATE, DELETE и SELECT. Необходимо также знать, как создавать представления только для чтения, используя SQL-команду CREATE VIEW и как создавать триггеры базы данных, используя PL/SQL.
Для выполнения практических упражнений, приведенных в этой статье, нужно проделать следующие действия для создания тестовой схемы и настройки сессии SQL*Plus:
SQL>
@c:\temp\courseware\m048.sql (для пользователей Windows) или
SQL>
@/tmp/courseware/m048.sql (для пользователей UNIX и Linux).
Если явно не указывать, что создаваемое представление является представлением только для чтения, то Oracle создаст представление, которые можно использовать для вставки, обновления или удаления строк базовой таблицы – другими словами обновляющее представление. Однако, представление не является автоматически или непосредственно обновляющим (inherently updatable), если Oracle не может корректно выполнить операцию вставки, обновления или удаления данных через представление в таблицу, лежащую в основе представления.
В следующем списке перечислены основные ограничения, которые необходимо учесть при создании запроса, определяющего представление, если вы хотите, чтобы представление было непосредственно обновляющим, без какой-либо дополнительной работы с вашей стороны. Определение непосредственно обновляющих представлений не может включать:
Операции
над множествами (UNION, UNION ALL, INTERSECT или MINUS)
Оператор
DISTINCT
Групповые
функции, такие как AVG, COUNT, MAX, MIN и т.д.
предложения GROUP BY, ORDER
BY, CONNECT BY или START WITH
ссылку
на выражение в списке SELECT
подзапрос
в списке SELECT
запрос
соединения (JOIN).
Кроме того, если в списке SELECT запроса, определяющего представление, содержатся виртуальные столбцы (выражения или ссылки на псевдостолбцы), то обновлять строки в базовой таблице, используя представление можно только в том случае, если в операторе UPDATE нет ссылки на виртуальные столбцы представления.
Используя текущую сессию SQL*Plus, введите следующую команду для создания непосредственно обновляющего представления, основанного на таблице CUSTOMERS.
CREATE VIEW mail_labels AS SELECT firstname||' '||lastname AS name, companyname, address, city||', '||state||' '||zipcode AS placeFROM customers;
В результате вы должны получить простое сообщение:
View created (Представление создано).
Вопрос: Объясните, почему представление ORDER_TOTALS, созданное следующей командой, не является непосредственно обновляющим.
CREATE VIEW order_totals (order_id, total) AS SELECT i.ord_ord_id, SUM(i.quantity * p.unitprice) FROM items i, parts p WHERE i.part_part_id = p.part_idGROUP BY i.ord_ord_id;
Ответ: Представление ORDER_TOTALS не является непосредственно обновляющим, потому что запрос, определяющий представление, содержит предложение GROUP BY.
Для просмотра информации о том, в какие столбцы обновляющего представления разрешена вставка, обновление и удаление, можно обратиться к представлению USER_UPDATABLE_COLUMNS словаря данных. Однако, если представление не является непосредственно обновляющим, то информация об обновляемых столбцах этого представления там отсутствует.
Используя SQL*Plus, введите следующий запрос для отображения информации об обновляемых столбцах нового представления MAIL_LABELS. Обратите внимание, что Oracle8i поддерживает операции INSERT, UPDATE и DELETE не для всех столбцов представления MAIL_LABELS.
SELECT column_name, updatable, insertable, deletable FROM user_updatable_columns WHERE table_name = 'MAIL_LABELS';
Результат запроса должен выглядеть следующим образом:
COLUMN_NAME UPD INS DEL_______________ __ __ __NAME NO NO NOCOMPANYNAME YES YES YESADDRESS YES YES YESPLACE NO NO NO
Используя SQL*Plus, введите команду, представленную в Листинге 1, для проверки работоспособности нового представления MAIL_LABELS. Обратите внимание, что некоторые предложения выполняются, в то время как другие возвращают сообщение об ошибке из-за ограничений, связанных с определением этого представления.
При создании обновляющего представления можно добавить предложение WITH CHECK OPTION, чтобы создать ограниченное представление и запретить определенные операции вставки и обновления в представление. Рассмотрим, например, следующее ограниченное представление:
CREATE VIEW contacts_ca AS SELECT * FROM customers WHERE state = 'CA' WITH CHECK OPTION;
Представление с ограничениями позволяет вставлять и обновлять только такие строки, которые это представление может, в свою очередь, выбрать.
Используя SQL*Plus, введите следующую команду для создания ограниченного представления, сонованного на таблице CUSTOMERS.
CREATE VIEW contacts_ca AS SELECT * FROM customers WHERE state = 'CA' WITH CHECK OPTION;
В результате вы должны получить простое сообщение:
View created.
Используя SQL*Plus, введите следующие команды, чтобы посмотреть, как для ограниченно обновляющего представления CONTACTS_CA выполняются и не выполняются различные операторы UPDATE.
UPDATE contacts_ca SET address = '100 Skyview Place', city = 'Brentwood', zipcode = '92011' WHERE cust_id = 4; UPDATE contacts_ca SET state = 'NY' WHERE cust_id = 4; ROLLBACK;
Результаты выполнения:
SQL> UPDATE contacts_ca 2 SET address = '100 Skyview Place', 3 city = 'Brentwood', 4 zipcode = '92011' 5 WHERE cust_id = 4; 1 row updated. SQL> UPDATE contacts_ca 2 SET state = 'NY' 3 WHERE cust_id = 4;UPDATE contacts_ca *ERROR at line 1:ORA-01402: view WITH CHECK OPTION where-clause violation SQL> ROLLBACK; Rollback complete.
Представление с соединением может быть обновляющим, но при этом накладываются некоторые дополнительные ограничения. По определению, запрос, описывающий представление с соединением, соединяет информацию из нескольких таблиц. Базовая таблица представления считается защищенной по ключу (key-preserved), если каждое значение первичного или уникального ключа базовой таблицы также является уникальным в результирующем множестве, выбираемом представлением с соединением - другими словами, если сущностная целостность базовой таблицы сохраняется представлением с соединением. Для лучшего понимания концепции защищенной по ключу таблицы в представлении с соединением, рассмотрим Рисунок 1.

Рисунок 1: Во-первых, проверим базовые таблицы и их первичные ключи. Целостность таблицы ORDERS обеспечивается ее первичным ключом, который включает столбец ORD_ID. Целостность таблицы ITEMS обеспечивается ее первичным ключом, который включает столбцы ORD_ORD_ID и ITEM_ID. Теперь проверим запрос, определяющий представление с соединением ORDER_ITEMS. Обратите внимание, что целостность таблицы ITEMS сохраняется в представлении с соединением, однако целостность таблицы ORDERS потеряна. Следовательно, таблица ITEMS считается защищенной по ключу таблицей по отношению к представлению ORDERS_ITEMS, в то время как таблица ORDERS таковой не является.
Кроме основных правил, применяемых для автоматически или непосредственно обновляющих представлений, для представлений с соединением, которые могут обновляться операторами INSERT, UPDATE и DELETE используются следующие правила:
Строки
в представлении с соединением, не являющееся ограниченным (представление с
соединением, созданное без предложения WITH CHECK OPTION), можно вставлять, если
оператор INSERT вставляет данные только в одну базовую защищенную по ключу
таблицу представления.
Обновлять
строки в представлении с соединением можно только в том случае, если оператор
UPDATE обновляет только столбцы базовой защищенной по ключу таблицы
представления. Кроме того, если представление с соединением является
ограниченным, то оператор UPDATE не может обновить столбцы, используемые в
условии соединения, а также столбцы, на которые в представлении ссылаются более
одного раза.
Строки
из представления с соединением можно удалять только в том случае, если
представление основано только на одной базовой защищенной по ключу таблице.
Концепцию защищенных по ключу таблиц и правил, изложенных выше, сложно понять без примеров, которые предоставляют следующие упражнения.
Используя SQL*Plus, введите следующие команды для создания обновляющего представления с соединением, а затем посмотрите, какие столбцы этого представления являются обновляемыми.
CREATE VIEW order_items AS SELECT o.ord_id, o.orderdate, i.item_id, i.part_part_id AS part_id, i.quantity FROM orders o, items i WHERE o.ord_id = i.ord_ord_id; SELECT column_name, updatable, insertable, deletable FROM user_updatable_columns WHERE table_name = 'ORDER_ITEMS'; В результате должно получиться следующее:
SQL> CREATE VIEW order_items AS 2 SELECT o.ord_id, 3 o.orderdate, 4 i.item_id, 5 i.part_part_id AS part_id, 6 i.quantity 7 FROM orders o, items i 8 WHERE o.ord_id = i.ord_ord_id; View created. SQL> SELECT column_name, updatable, insertable, deletable 2 FROM user_updatable_columns 3 WHERE table_name = 'ORDER_ITEMS'; COLUMN_NAME UPD INS DEL___________ ___ ___ ___ORDERDATE NO NO NOITEM_ID YES YES YESPART_ID YES YES YESQUANTITY YES YES YESORD_ID NO NO NO
Все предыдущие разделы этой статьи подробно рассматривали непосредственно обновляющие представления. Рассмотрим теперь, как можно сделать любое представление обновляющим, вне зависимости от его сложности, используя триггеры INSTEAD OF.
Триггер INSTEAD OF является специальным типом триггера, который говорит Oracle, как обрабатывать DML-операцию (INSERT, UPDATE или DELETE), выполняемую представлением. Например, допустим, что у вас есть представление, которое соединяет информацию из двух различных таблиц. При выполнении в представлении оператора INSERT, содержащего поля обеих базовых таблиц, Oracle8i не будет знать точно, каким образом вставить эти новые строки в основные базовые таблицы. Создав триггер INSTEAD OF для представления, вы сможете точно сказать Oracle8i, как обрабатывать такие операторы INSERT. Для создания триггера INSTEAD OF для представления, используется следующий синтаксис SQL-команды CREATE TRIGGER:
CREATE [OR REPLACE] TRIGGER [schema.]trigger INSTEAD OF {DELETE|INSERT|UPDATE [OF column [,column]... ]} [OR {DELETE|INSERT|UPDATE [OF column [,column] ... ]} ] ... ON [schema.]view ... trigger body ...
Замечание: Если вы создаете представление, определяющий запрос которого ссылается на представление, имеющее триггеры INSTEAD OF, то вы все равно должны создавать триггеры INSTEAD OF для нового представления, даже если новое представление является непосредственно обновляющим.
Используя SQL*Plus, введите команды, представленные в Листинге 2, для создания представления, соединяющего поля из таблиц CUSTOMERS и ORDERS, и триггера INSTEAD OF для обработки операторов INSERT, адресованных этому представлению. Обратите внимание, что триггер выполняет два различных оператора INSERT с соответствующими полями для двух базовых таблиц представления.
Перед тем как проверять новое представление ORDER _INFO, и связанный с ним триггер INSTEAD OF, используйте SQL*Plus для ввода следующего запроса, который отобразит информацию об обновляемых столбцах представления.
SELECT column_name, updatable, insertable, deletable FROM user_updatable_columns WHERE table_name = 'ORDER_INFO';В результате должно получиться следующее:
COLUMN_NAME UPD INS DEL___________ ___ ___ ___CUST_ID NO YES NOCOMPANYNAME NO YES NOLASTNAME NO YES NOFIRSTNAME NO YES NOORD_ID YES YES YESORDERDATE YES YES YESSHIPDATE YES YES YESPAIDDATE YES YES YES
Используя SQL*Plus, введите следующий оператор INSERT и последующие запросы для проверки работоспособности триггера INSTEAD OF, связанного с представлением ORDER_INFO.
INSERT INTO order_info ( cust_id, companyname, lastname, firstname, ord_id, orderdate )VALUES ( 10, 'Acme', 'Schwartz', 'Elsie', 6, '13-DEC-97' ); SELECT cust_id, lastname FROM customers WHERE cust_id = 10; SELECT ord_id, orderdate FROM orders WHERE ord_id = 6;
В результате должно получиться следующее:
SQL> INSERT INTO order_info 2 ( cust_id, 3 companyname, 4 lastname, 5 firstname, 6 ord_id, 7 orderdate ) 8 VALUES 9 ( 10, 10 'Acme', 11 'Schwartz', 12 'Elsie', 13 6, 14 '13-DEC-97' ); 1 row created. SQL> SELECT cust_id, lastname 2 FROM customers 3 WHERE cust_id = 10; CUST_ID LASTNAME _____ _______ 10 Schwartz SQL> SELECT ord_id, orderdate 2 FROM orders 3 WHERE ord_id = 6; ORD_ID ORDERDATE _____ ________6 13-DEC-97
Помните следующие важные моменты:
Представление
не является непосредственно обновляющим, за исключением случаев, когда Oracle8i
может корректно выполнить операторы INSERT, UPDATE или DELETE через
представление в базовую таблицу, на которой оно основано.
Представление
с ограничением (WITH CHECK OPTION) позволяет выполнять только те операторы
INSERT, UPDATE и DELETE которые создают строки, которые это представление может
в свою очередь выбрать.
Кроме
основных правил для непосредственно обновляющих представлений, представление с
соединением, которое является непосредственно обновляющим операторами INSERT,
UPDATE и DELETE должно удовлетворять дополнительным ограничениям.
Любое
представление можно сделать обновляющим, вне зависимости от его сложности,
используя триггеры INSTEAD OF.
Контрольные вопросы и ответыУпражнение 2:Вопрос: Объясните, почему столбцы NAME и PLACE представления MAIL_LABELS, созданного в упражнении 1, закрыты для вставки, обновления и удаления. Ответ: Столбцы NAME и PLACE не являются обновляемыми, потому что они являются виртуальными столбцами, то есть при создании представления в списке SELECT определяющего запроса для этих столбцов использовались выражения. |
Упражнение 4:Вопрос: Почему Oracle возвращает ошибку при попытке выполнить следующее предложение? CREATE VIEW contacts_ca AS SELECT * FROM customers WHERE state = 'CA' WITH READ ONLYWITH CHECK OPTION; Ответ: Вы не можете создать представление только для чтения с ограничением. Ограничения используются только для обновляющих представлений. |
Упражнение 5:Вопрос: Почему Oracle выполняет первый оператор UPDATE в этом упражнении, но запрещает выполнение второго оператора UPDATE? Ответ: После выполнения первого оператора UPDATE представление может возвратить эту строку из базовой таблицы CUSTOMERS, поскольку значением столбца STATE в обновленной пользователем записи остается CA. Однако Oracle не выполняет второй оператор UPDATE, потому что значением столбца STATE в обновленной пользователем записи становится NY, и представление не может больше выбирать эту запись. |
Упражнение 6:Вопрос: Результат выполнения запроса в этом упражнении показывает, что все столбцы представления, выбранные из базовой таблицы ITEMS, являются обновляемыми, а все столбцы, происходящие из таблицы ORDERS, необновляемые. Почему? Ответ: Представление сохраняет целостность базовой таблицы ITEMS, но не сохраняет целостность базовой таблицы ORDERS. |
Упражнение 8:Вопрос: Почему для всех столбцов представления ORDER_INFO запрещено обновление и удаление, хотя для представления существует триггер INSTEAD OF? Ответ: Триггер INSTEAD OF для представления не обрабатывает операторы UPDATE и DELETE, только операторы INSERT. |
Упражнение 9:Вопрос: Можете ли вы объяснить, что происходит, когда выполняется оператор INSERT в этом упражнении? Ответ: Оператор INSERT обращается к представлению ORDER_INFO, которое имеет соответствующий триггер INSTEAD OF, ORDER_INFO _INSERT, для обработки оператора INSERT. В теле триггера ORDER_INFO_INSERT значения оператора INSERT, вызвавшего триггер, вставляются в две базовые таблицы представления CUSTOMERS и ORDERS двумя отдельными операторами INSERT. Вопрос: Что произойдет, если для представления ORDER_INFO не будет создан триггер ORDER_INFO_INSERT, перед тем как выполнить оператор INSERT из предыдущего примера? Ответ: Oracle вернет ошибку, поскольку он не сможет вставить значения оператора INSERT в представление ORDER_INFO, которое соединяет данные из двух таблиц. |
Приложения
Листинг 1
Описание таблицы recipes.
SQL> DESC recipes Name Null? Type ----------------- --------- --------------- ID NOT NULL NUMBER NAME NOT NULL VARCHAR2(100) PREP_TIME_MINUTES NUMBER SERVINGS NUMBER DESCRIPTION VARCHAR2(1000) COOKING_INSTRUCTIONS CLOB DISH_IMAGE ORDSYS.ORDIMAGE
Листинг 2
Создание триггера INSTEAD OF для обновляющего представления
CREATE VIEW order_info AS SELECT c.cust_id, c.companyname, c.lastname, c.firstname, o.ord_id, o.orderdate, o.shipdate, o.paiddate FROM customers c, orders o WHERE c.cust_id = o.cust_cust_id; CREATE OR REPLACE TRIGGER order_info_insertINSTEAD OF INSERT ON order_infoDECLARE duplicate_info EXCEPTION; PRAGMA EXCEPTION_INIT (duplicate_info, -00001);BEGIN_ Insert appropriate fields into each base table._ Exception handler returns error messages for_ attempts to insert duplicate information. INSERT INTO customers (cust_id, companyname, lastname, firstname) VALUES ( :new.cust_id, :new.companyname, :new.lastname, :new.firstname ); INSERT INTO orders (ord_id, orderdate, shipdate, paiddate) VALUES ( :new.ord_id, :new.orderdate, :new.shipdate, :new.paiddate );EXCEPTION WHEN duplicate_info THEN RAISE_APPLICATION_ERROR ( num => -20107, msg => 'Duplicate customer or order ID' );END order_info_insert;/ View created.Trigger created.
Об авторе:
Стив Бобровский является главным администратором компании Animated Learning (http://www.animatedlearning.com).
Он автор книг издательства Oracle Press “Архитектура Oracle8”, “Oracle8i для
Windows NT. Руководство для начинающих”, “Oracle8i для Linux. Руководство для
начинающих”. Познакомиться с описанием этих книг можно в он-лайне по адресу www.osborne.com/oracle/