Классика баз данных - статьи

       

Что такое подставляемость?


При объявлении иерархии типов, мы начинаем с корневого типа, из которого выводятся все другие подтипы. Например, в языке Java все классы (приблизительно аналогичные "объектным типам" Oracle) происходят от корневого класса Object. В Oracle, где объектная модель располагается на вершине реляционной базы данных, не существует встроенной глобальной иерархии. Поэтому, каждый раз, когда мы работаем с объектными типами, необходимо объявлять свой собственный корень.

В этой статье мы рассмотрим очень простую иерархию типов, показанную на Рисунке 1 (и объявленную в файле food.ot, который находится в прилагаемом Download файле).

В этой иерархии тип еда (food), food_t, является корнем. Тип десерт (dessert), dessert_t, является подтипом еды, а пирожное (cake), описанное как cake_t, является в свою очередь подтипом dessert_t. Ниже приведены объявления этих типов (показаны только атрибуты, без связанных с ними PL/SQL методов):

CREATE TYPE food_t AS OBJECT ( name VARCHAR2(100), food_group VARCHAR2 (100), grown_in VARCHAR2 (100) ) NOT FINAL ; / CREATE TYPE dessert_t UNDER food_t ( contains_chocolate CHAR(1), year_created NUMBER(4) ) NOT FINAL ; / CREATE TYPE cake_t UNDER dessert_t ( diameter NUMBER, inscription VARCHAR2(200) ) ; /

Каждый тип имеет свои собственные характерные для данного типа атрибуты. Каждый подтип, не забывайте, также наследует атрибуты своего супертипа(ов). Поэтому, если нужно присвоить значение объекту типа cake, то необходимо указать семь атрибутов, как показано ниже:

DECLARE my_favorite cake_t := cake_t ( 'Marzepan Delight', 'CARBOHYDRATE', 'Swedish Bakery', 'N', 1634, 8, 'Happy Birthday!' ); BEGIN DBMS_OUTPUT.put_line (my_favorite.NAME); DBMS_OUTPUT.put_line (my_favorite.inscription); END; /

Обратите внимание, что я отображаю и ссылаюсь на атрибуты как базового типа еда, так и подтипа пирожное. Они все одинаково доступны мне в объекте типа пирожное.

Эту иерархию нужно понимать следующим образом: пирожное является типом десерта, который, в свою очередь является типом еды.
Но не все десерты являются пирожными, и не всякая еда является десертом (отложим сейчас очевидные культурные сложности, например, что-нибудь, что не считается десертом в Соединенных Штатах, может являться таковым, скажем, в Эквадоре). Любые характеристики еды приложимы к пирожному, но не все характеристики пирожного обязательно имеют смысл для еды, к примеру, такой как огурец.

После того как иерархия объявлена, мы можем работать с ней и выполнять изменения типов в этой иерархии. В некоторых случаях, возможно, потребуется выбирать и просматривать все типы по всей иерархии. В других случаях - только обновлять определенный уровень иерархии, такой как пирожные. И, наконец, бывают ситуации, когда необходимо работать с, скажем, всеми десертами, которые не являются пирожными. И это приводит нас прямо к концепции подставляемости (substitutability)

Супертип является подставляемым, если один из его подтипов может заменить его в некоторых ситуациях, например, при присваивании столбцу или программной переменной, объявленной как супертип (а не как этот конкретный подтип).



Допустим, я создаю таблицу объектов типа food_t:

CREATE TABLE sustenance OF food_t;

Я могу теперь вставить строки в эту таблицу следующим образом:

BEGIN INSERT INTO sustenance VALUES (food_t ( 'Brussel Sprouts', 'VEGETABLE', 'farm' ) ); INSERT INTO sustenance VALUES (dessert_t ( 'Jello', 'PROTEIN', 'bowl', 'N', 1887 ) ); INSERT INTO sustenance VALUES (cake_t ( 'Marzepan Delight', 'CARBOHYDRATE', 'bakery', 'N', 1634, 8, 'Happy Birthday!' ) ); END; /

После запуска этого кода в моей таблице будет три строки: объект еда, объект десерт и объект пирожное. В этом блоке кода, я подставляю мои подтипы вместо супертипа в двух вставках. Это не вызывает ошибки, поскольку пирожные и десерты являются типами еды.

Я могу выполнить запрос к этой таблице в SQL*Plus и он покажет мне все атрибуты типа еда (и только) для трех строк.

SQL> SELECT * FROM sustenance; NAME FOOD_GROUP GROWN_IN ------------------------- ------------------ ------------- Brussel Sprouts VEGETABLE farm Jello PROTEIN bowl Marzepan Delight CARBOHYDRATE bakery



Я могу также использовать преимущества подставляемости в PL/SQL блоках. В следующем коде, я объявляю еду, но инициализирую ее десертом, более конкретным типом еды.

DECLARE mmm_good food_t := dessert_t ( 'Super Brownie', 'CARBOHYDRATE', 'my oven', 'Y', 1994); BEGIN DBMS_OUTPUT.PUT_LINE ( mmm_good.name); END; /

А вот пример подставляемости в PL/SQL коллекциях:

DECLARE TYPE foodstuffs_nt IS TABLE OF food_t; fridge_contents foodstuffs_nt := ( food_t ( 'Eggs benedict', 'PROTEIN', 'Farm'), dessert_t ( 'Strawberries and cream', 'FRUIT', 'Backyard', 'N', 2001), cake_t ( 'Chocolate Supreme', 'CARBOHYDRATE', 'Kitchen', 'Y', 2001, 8, 'Happy Birthday, Veva')); BEGIN FOR indx IN fridge_contents.FIRST .. fridge_contents.LAST LOOP DBMS_OUTPUT.PUT_LINE ( fridge_contents(indx).name); END LOOP; END; /

Теперь рассмотрим вставки, которые не работают. Допустим, я создал объектную таблицу десертов:

CREATE TABLE sweet_nothings OF dessert_t; /

Если теперь я попытаюсь вставить объект типа еда, Oracle выдаст ошибку, как показано ниже:

BEGIN INSERT INTO sweet_nothings VALUES (dessert_t ( 'Jello', 'PROTEIN', 'bowl', 'N', 1887 ) ); INSERT INTO sweet_nothings VALUES (food_t ( 'Brussel Sprouts', 'VEGETABLE', 'farm' ) ); END; / PL/SQL: ORA-00932: inconsistent datatypes

Я получил эту ошибку потому, что любой десерт является едой, но не любая еда является десертом. Я не могу вставить объект типа food_t в столбец типа dessert_t.

Теперь рассмотрим аналогичную ситуацию, в PL/SQL. Я объявляю в своей программе объект типа еда и инициализирую его десертом. Обратите внимание, что я указал Y или "Yes, it sure does!" ("Да, конечно, содержит!") для атрибута contains_chocolate (содержит_шоколад). Однако, если я попытаюсь в своем коде указать этот атрибут, характерный для десерта, PL/SQL выдаст мне ошибку.

SQL> DECLARE 2 -- Опять я подставляю, но на этот раз 3 -- я пытаюсь получить доступ к атрибуту десерта. 4 mmm_good food_t := 5 dessert_t ( 6 'Super Brownie', 7 'CARBOHYDRATE', 8 'my oven', 'Y', 1994); 9 BEGIN 10 DBMS_OUTPUT.PUT_LINE ( 11 mmm_good.contains_chocolate); 12 END; 13 / mmm_good.contains_chocolate); * ERROR at line 11: ORA-06550: line 11, column 16: PLS-00302: component 'CONTAINS_CHOCOLATE' must be declared

Как вы можете заметить, типы являются, как правило, подставляемыми (то есть, вы можете подставить подтип для его супертипа). Преимущества подставляемости можно использовать в объектных типах, объявленных как атрибуты объектных типов, столбцы таблицы или строки в таблицах и коллекциях.

В описании самого объектного типа Oracle не предоставляет никакого способа для отключения подставляемости; любой объектный тип является теоретически или потенциально подставляемым. С другой стороны, Oracle предлагает способ ограничить подставляемость и даже сделать ее невозможной при объявлении использования этого объектного типа.


Содержание раздела