Объектные модели данных
Изменение и удаление типов. Зависимости объектов
В наших первых примерах типы не содержат методов. В общем случае в типе, как и в пакете, создаётся спецификация и тело метода.
Объектные типы можно изменять оператором 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 ошибкой не будет.
Конструкторы по умолчанию
Метод конструктора, как всегда в ООП, предусматривается для любого объектного типа и возвращает новый экземпляр этого типа.
Для демонстрации применения конструктора по умолчанию создадим тип 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):
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):
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).
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.