Кубанский государственный университет
Опубликован: 24.12.2013 | Доступ: свободный | Студентов: 681 / 8 | Длительность: 24:28:00
Лекция 10:

Объектные модели данных

Изменение и удаление типов. Зависимости объектов

В наших первых примерах типы не содержат методов. В общем случае в типе, как и в пакете, создаётся спецификация и тело метода.

Объектные типы можно изменять оператором ALTER TYPE и удалять оператором DROP TYPE.

Форматы некоторых команд:

[ALTER TYPE имя_типа COMPILE [SPECIFICATION|BODY]

Компилирует спецификацию или тело типа. Если не указано ни одно из слов SPECIFICATION, BODY, то компилируются оба. Последовательность компиляции такая же как для пакетов.

ALTER TYPE имя_типа
REPLACE AS OBJECT (спецификация_объектного_типа)

Новое описание, заданное спецификацией_объектного_типа должно во всем, кроме дополнительных методов совпадать с исходным.

После выполнения этого оператора, если тело типа существовало ранее, оно становится не действительным (INVALID).

Удаление типа.

DROP TYPE  [имя_схемы.]имя_типа [FORCE]

Если указан параметр FORCE, то тип удалится, даже если существуют зависимые от него объекты. Естественно, все зависимые объекты становятся INVALID.

Пример зависимости объектов. Создаем два независимых типа

CREATE OR REPLACE TYPE Objl AS OBJECT ( Cl NUMBER, C2 VARCHAR2(5)
);
CREATE OR REPLACE TYPE Obj2 AS OBJECT (
Cl CHAR(2),
C2 VARCHAR2(5)
);

и зависящий от них Obj3

CREATE OR REPLACE TYPE Obj3 AS OBJECT (
al Objl, a2 Obj2
);

Тогда попытка удаления

DROP TYPE Objl;

вызовет ошибку, так как от Objl зависит Obj3.

А вот удаление сначала Obj3, затем Objl или Obj2 ошибкой не будет.

Конструкторы по умолчанию

Метод конструктора, как всегда в ООП, предусматривается для любого объектного типа и возвращает новый экземпляр этого типа.

Удаление типа Obj1

увеличить изображение
Рис. 10.33. Удаление типа Obj1

Для демонстрации применения конструктора по умолчанию создадим тип address_type, затем на его основе объектный тип person и таблицу peoples со столбцом этого типа.

CREATE   TYPE  address_type   as object (
zipcode  NUMBER(5),
country  VARCHAR2(20),
city  VARCHAR2(30),
street VARCHAR2(30),
numb NUMBER(4)
);

CREATE   TYPE person AS OBJECT (
 
name VARCHAR2(40), birthday DATE, address address_type, MEMBER FUNCTION Age

— дата рождения
спецификация метода, возвращающего возраст
 
RETURN NUMBER  — возвращаемое значение
);

Поскольку определена спецификация типа, содержащего функцию-член типа, необходимо еще создать тело типа:

CREATE OR REPLACE TYPE BODY person IS — задание методов
MEMBER FUNCTION Age RETURN NUMBER IS
BEGIN    -- вычисление возраста
RETURN ROUND(MONTHS_BETWEEN(sysdate, birthday)/12);
END;
END;

А теперь посмотрите сами описание нового типа, используя команду DESC person или без сокращений DESCRIBE person. Создадим таблицу типа person командой CREATE TABLE peoples OF person; Проверим ее структуру (таблица 10.12):

Таблица 10.12. Структура таблицы peoples
DESC People
Table Column Data Type Length Precision Scale Primary Key Nullable Default Comment
PEOPLES NAME Varchar2 40 - - - + - -
BIRTHDAY Date 7 - - - + - -
ADDRESS Address_Type 1 - - - + - -
1-3

В объектных таблицах работают ограничения CHECK, PRIMARY KEY и UNIQUE. Например, создадим таблицу peoplesl c первичным ключом:

CREATE TABLE peoplesl OF person (name   PRIMARY KEY);

Проверим ее структуру для сравнения с peoples. В столбце Primary Key в строке NAME появится отметка первичного ключа.

В объектных таблицах вставка строки "по-старому" не удастся. Например, вставка

INSERT INTO peoples
VALUES    ('Сидоров И.  П.', '22.11.80',350000,'Россия', 'Краснодар','Ставропольская',  14 9);

вызывает появление ошибки.

Теперь вставка должна производиться так:

INSERT INTO peoples
VALUES('Сидоров И.П.', '22.11.80', address_type (35000, 'Россия', 'Краснодар','Ставропольская',  14 9)
);

Читать как в обычных реляционных таблицах также не удастся. Так, команда

SELECT * FROM peoples;

вызывает сообщение об ошибке.

Тот же результат дают запросы:

SELECT name, birthday, address   FROM peoples; 
SELECT name, birthday, address.country FROM peoples;

А вот следующая команда позволяет работает нормально (таблица 10.13):

Таблица 10.13. Выборка из таблицы peoples
SELECT name, birthday,p.address.countryFROM peoples p;
NAME BIRTHDAY ADDRESS.COUNTRY
Сидоров И.П. 22.11.80 Россия

Обращение к подобъектам производится с использованием точечного синтаксиса и псевдонимов:

SELECT p.address.country FROM peoples p;

А вот без псевдонима никак нельзя. Следующий запрос вызывает появление ошибки:

SELECT peoples.address.country FROM peoples;
Как хранятся объектные таблицы

Давайте посмотрим, что произошло при создании объектной таблицы peoples. Для этого обратимся к представлению словаря user_tab_col (таблица 10.14), которое перечисляет все столбцы указанной таблицы, в том числе, скрытые (hidden).

Таблица 10.14. Представление user_tab_cols
SELECT column_name,  hidden_column, data_typeFROM user_tab_colsWHERE table name =  'PEOPLES';
COLUMN_NAME HIDDEN_COLUMN DATA_TYPE
SYS_NC_OIDS YES RAW
SYS_NC_ROWINFO$ YES PERSON
NAME NO NO VARCHAR2
BIRTHDAY NO DATE
ADDRESS YES ADDRESS_TYPE
SYS_NC00006$ YES NUMBER
SYS_NC00007$ YES VARCHAR2
SYS_NC00008$ YES VARCHAR2
SYS_NC00009$ YES VARCHAR2
SYS NC00010$ YES NUMBER

Итак, создана не совсем обычная реляционная таблица peoples. В ней имеются скрытые столбцы.

Столбцы name и birthday это обычные реляционные столбцы и читаются обычным запросом. Столбец address и SYS_NC_ROWINFO$ таким запросом не читаются. Столбец SYS_NC_OID$, по-видимому, содержит объектные идентификаторы строк таблицы. Последние пять скрытых столбцов по типам подозрительно напоминают столбцы zipcode, country, city, street и numb, определённые в типе address_type. Проверьте это предположение, выполнив запрос

SELECT SYS_NC00006$,  SYS_NC00007$, SYS_NC00008$, SYS_NC00009$, SYS_NC00010$
FROM peoples;

Вы увидите введённые вами данные.

Из сказанного следует, что объектно-реляционная таблица это не тривиальное расширение реляционной таблицы.

Индексы и ограничения целостности

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

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

CREATE INDEX country_idx ON peoples (address.country);

В этих же ситуациях можно определять и вводить ограничения целостности, например,

ALTER TABLE peoples
ADD CONSTRAINT peoples_cons1
CHECK (address.country IS NOT NULL);

При создании таблицы также следует использовать ограничения целостности уровня таблицы.

NULL'bi в объектных таблицах

В объектной таблице столбцы, коллекции или элементы коллекций могут принимать значения NULL, если они были установлены в NULL или не были инициализированы вообще. Различают объект со всеми значениями NULL и NULL-объект, имеющий значение null.

Пример вставки значений NULL:

INSERT INTO peoples
VALUES(null, '22.11.80',
address_type (null,  'Россия', null,null,null));
Ссылочные типы

Мы уже упоминали, что версия Oracle XE, видимо, не предназначалась для работы с объектно-реляционной моделью. Не всё, что реализуется в основных версиях СУБД, в ней удаётся. Правда, замеченные нами проблемы касаются работы оператора SELECT и процедуры PUT_LINE. В предыдущих разделах вы видели не совсем естественную работу SELECT с объектными таблицами. Здесь мы не сможем запрашивать ещё и данные ссылочного типа. С ними не работает и процедура PUT_LINE.

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

Получение ссылки:

DECLARE RefVar REF person; BEGIN
SELECT ref(P)    INTO RefVar FROM peoples P WHERE P.address.zipcode=35000;
END;
Добавим в этот анонимный блок обращение по ссылке:
DECLARE RefVar REF person; BEGIN
SELECT ref(P)   INTO RefVar FROM peoples P WHERE P.address.zipcode=35000; 
UPDATE   peoples P SET P.address.numb=166 WHERE ref(P)= RefVar;
END;

Создадим еще один тип:

CREATE TYPE project AS OBJECT ( projno NUMBER(5), Emp_ref REF person );

и объектную таблицу:

CREATE TABLE projects OF project;

Занесем один объект в таблицу projects:

DECLARE refVar REF person; BEGIN
SELECT ref(P) INTO refVar FROM peoples P
WHERE P.name='C^4opoB И.П.',-
INSERT INTO projects VALUES (1, refvar);
END;

Команда SELECT * FROM projects в других версиях Oracle даст что-нибудь вроде:

Но, как уже говорилось, в Oracle XE она не работает.

PROJNO EMP_REF
-------------------------------------------------------------------------------------
1  00022 02087 6D564 02DC6F0403E034 08002 0C944 037 6D56402DC6D
Оператор deref извлекает объект по заданной для него ссылке

Например, выборку из таблиц projects и peoples, связанных ссылками можно было бы организовать так:

SELECT projno, deref(Emp_ref) FROM   projects P WHERE P.Emp_ref    IS NOT DANGLING;

Благодаря использованию deref в запросе упоминается только таблица projects, а данные берутся из обеих таблиц, связанных ссылкой.

К сожалению, и этот запрос в Oracle XE не работает.

Поскольку идентификатор таблицы, на которую ссылаются, содержится в ссылке, то ссылка в поле P.emp_ref может указывать на строку любой объектной таблицы типа person, а не одной таблицы peoples.

Остается разъяснить предикаты is dangling и is not dangling. Если объект, на который указывает ref удалить, то ссылка будет указывать на несуществующий объект. Такую ссылку называют висячей (dangling). Предикат is dangling принимает истинное значение, если ссылка висячая.

Пример: Установка значений NULL для всех висячих ссылок

BEGIN
UPDATE projects SET emp_ref = NULL
WHERE emp_ref IS DANGLING;
END;
Объектные идентификаторы OID

Пакет DBMS_ROWID позволяет узнать объектный идентификатор любой таблицы, и реляционной и объектной. Естественно, по OID восстанавливается имя таблицы (peoples в нашем примере в листинге 10.13).

SELECT DBMS_ROWID.ROWID_Object(RowID)
FROM peoples
WHERE RowNum=1;


select Object_Name
from USER_OBJECTS
where Object_ID=13698;

Results:
OBJECT_NAME
PEOPLES

Пример 10.13.