© Владимир
Пржиялковский,
координатор Евро-Азиатской Группы Пользователей Oracle,
преподаватель УКЦ Interface Ltd.
В версии 8 в Oracle появилась возможность хранения неатомарных (нескалярных) значений в поле таблицы, а именно объекта в смысле объектного подхода (в рамках так называемой “объектно-реляционной модели” Oracle). Некоторые существенные пробелы этой первой реализации были устранены в версии 9. Примеры ниже используют возможности версии 9.2.
Сразу надо предостеречь от преувеличений достоинств объектного подхода в базах данных вообще. Действительно, неискушенный читатель некоторых руководств или рекламных материалов быстро впадет в недоумение: зачем же такие маститые разработчики СУБД, как фирмы IBM, Informix или Oracle так долго занимались табличной организацией данных, когда все это время рядом существовала более совершенная, удобная и т. д. объектная, первая реализация которой фирмой Xerox известна с 1980 года?
Непредвзятый ответ состоит в том, что ни табличная организация (часто вольно называемая “реляционной” применительно к конкретным СУБД), ни объектная не являются универсально “хорошими”, и что имеются свои достоинства и недостатки у одной и у другой. Некоторые соображения относительно областей применения обоих подходов к хранению данных можно найти в статье “Что объектам здорово, то реляциям смерть, и наоборот, и еще пол-оборота”.
В целом объектная реализация в Oracle традиционна для объектного подхода вообще. В основе лежит понятие объекта как совокупности свойств (атрибутов), причем действия с объектом регламентируются формулируемым набором методов (процедур или функций). Тип объекта задается сохраняемым в БД объектом TYPE.
Рассмотрим схему БД, где хранятся данные о сотрудниках и отделах. Будем работать в схеме SCOTT, из которой на время нужно удалить таблицы EMP и DEPT (позже мы их восстановим).
Предположим, что и те, и другие имеют адреса: сотрудники - домашний, а отделы – юридический. Адрес имеет несколько полей (например, “индекс”, “район”, “населенный пункт”, “место”). В традиционной табличной реализации есть два способа промоделировать наличие адреса:
- включить одинаковые группы полей в таблицы сотрудников и отделов;
- создать отдельную таблицу адресов и включить в таблицы сотрудников и отделов
ссылки на нее.
Первое решение неудобно тем, что адрес теряет свою идентичность: неудобно, например, сравнивать адреса, особенно в разных таблицах. Второе решение искусственно, если только не считать адреса самостоятельными объектами моделирования.
Объектные возможности последних версий Oracle дают возможность более приемлемой альтернативы. Для описания адреса создадим тип (здесь и далее предполагается использование в качестве рабочего инструмента SQL*Plus):
CREATE TYPE address_typ AS OBJECT (
zip CHAR(6),
location VARCHAR2(200))
/
Воспользуемся этим типом для описания сотрудников и отделов:
CREATE TABLE dept (
dname VARCHAR2(50),
deptno NUMBER CONSTRAINT pk_dept PRIMARY KEY,
addr address_typ);
CREATE TABLE emp (
ename VARCHAR2(50),
empno NUMBER CONSTRAINT pk_emp PRIMARY KEY,
deptno NUMBER CONSTRAINT fk_emp REFERENCES dept,
home address_typ);
Проверим описания созданных объектов:
DESCRIBE address_typ
DESCRIBE dept
DESCRIBE emp
Пример заведения сотрудников и отделов:
INSERT INTO dept VALUES (
'Sales',
10,
address_typ('123456', 'Boston 123... '));
INSERT INTO emp VALUES (
'Smith',
1001,
10,
address_typ('123333', 'Boston 567... '));
Здесь выражение ADDRESS_TYP('123333', 'Boston 567... ') означает обращение к конструктору объекта, то есть к функции, автоматически создаваемой СУБД при заведении нового типа для возможности создавать новые объекты этого типа с нужными значениями атрибутов. Понятие конструктора общепринято в объектном подходе. В приведенных предложениях INSERT простановку адреса можно оформить чуть иначе, добавив, в соответствии с духом объектного подхода, ключевое слово NEW перед обращением к конструктору:
INSERT INTO emp VALUES (
'Allen',
1002,
10,
NEW address_typ('123456', 'Boston 123... '));
Проверка:
COLUMN dname FORMAT A20
COLUMN ename FORMAT A20
COLUMN addr FORMAT A40
COLUMN home FORMAT A40
SELECT * FROM dept;
SELECT * FROM emp;
Другие примеры:
SELECT ename, home FROM emp;
SELECT e.ename, d.dname FROM emp e, dept d WHERE e.home = d.addr;
SELECT e.ename, e.home.zip FROM emp e;
UPDATE emp
SET home = address_typ('123457', 'Boston 777... ')
WHERE ename = 'Allen';
UPDATE emp e SET e.home.zip = '123458' WHERE ename = 'Allen';
Если адрес интересует нас как самостоятельная сущность, а не атрибут прочих сущностей, созданный для адреса тип можно использовать для создания таблиц объектов:
CREATE TABLE addr_list1 OF address_typ;
CREATE TABLE addr_list2 OF address_typ;
Таблицы объектов в Oracle было бы точнее называть списками объектов, так как это всегда таблицы ровно из одного столбца объектного типа.
Заполнение данными происходит как и ранее:
INSERT INTO addr_list1 VALUES
(NEW address_typ('123456', 'Boston 123... '));
или
INSERT INTO addr_list1 VALUES
(address_typ('123458', 'Boston 123... '));
INSERT INTO addr_list2 VALUES
(address_typ('123333', 'Boston 567... '));
Просмотр:
COLUMN location FORMAT A30
SELECT * FROM addr_list1;
SELECT VALUE(a) FROM addr_list1 a;
SELECT e.ename, e.home
FROM addr_list1 a, emp e
WHERE VALUE(a) = e.home;
(Функция VALUE специально придумана для возвращения значений объектов, а не атрибутов объектов по отдельности).
Объекты, заведенные в объектных таблицах, имеют одно преимущество перед объектами, указанными как атрибут строки: на них можно ссылаться. Ссылка есть уникальный внутренний идентификатор объекта, и получить его можно с помощью функции REF:
COLUMN ref FORMAT A90
COLUMN value FORMAT A40
SELECT REF(a) ref, VALUE(a) FROM addr_list1 a;
Теперь можно поменять описание таблицы, например, DEPT, чтобы она заимствовала адреса отделов из имеющегося списка, а не хранила вместе со своими данными:
ALTER TABLE dept DROP (addr);
ALTER TABLE dept ADD (addr REF address_typ SCOPE IS addr_list1);
SELECT * FROM dept;
UPDATE dept d
SET d.addr =
(SELECT REF(a)
FROM addr_list1 a
WHERE VALUE(a)= address_typ('123458', 'Boston 123... '))
WHERE d.deptno = 10;
SELECT * FROM dept;
Фраза SCOPE IS при определении типа как ссылки на существующий объект необязательна, но позволяет фактически ссылаться только на объекты какой-нибудь объектной таблицы.
Раскрытие ссылки делается с помощью специальной функции DEREF:
COLUMN deref(addr) FORMAT A40
SELECT d.dname, DEREF(addr) FROM dept d;
Однако при обращении к нижележащим атрибутам раскрытие может выполняться и неявно (неявное преобразование типов, присутствующее в Oracle-диалекте SQL):
SELECT d.dname, d.addr.zip FROM dept d;
вместо более правильного
SELECT d.dname, DEREF(d.addr).zip FROM dept d;
Выше было рассмотрено определение типа, содержащее описание атрибутов (“свойств”). Создадим тип сотрудников, в котором определен еще и метод:
CREATE TYPE employee_typ AS OBJECT (
ename VARCHAR2(50),
hiredate DATE,
deptno NUMBER,
home REF address_typ,
MEMBER FUNCTION days_at_company RETURN NUMBER)
/
Для описания тела метода-функции необходимо создать тело типа (аналогия пакет – тело пакета в PL/SQL):
CREATE TYPE BODY employee_typ IS
MEMBER FUNCTION days_at_company RETURN NUMBER IS
BEGIN
RETURN TRUNC(SYSDATE-hiredate);
END;
END;
/
Создадим таблицу объектов-сотрудников:
DROP TABLE emp;
CREATE TABLE emp OF employee_typ;
INSERT INTO emp VALUES (
'Scott',
SYSDATE,
10,
(SELECT REF(a) FROM addr_list1 a
WHERE VALUE(a) = address_typ('123458', 'Boston 123... ')));
Пример обращения к методу:
COLUMN home.location FORMAT A20
SELECT e.ename, e.home.location, e.days_at_company() FROM emp e;
Переводить в существующей БД табличные описания данных в объектные не всегда возможно, а иногда и не нужно. В силу разных обстоятельств может оказаться удобной имитация объектов на основе данных, хранимых в традиционных таблицах. Тогда к одним и тем же данным можно обращаться и через объектный интерфейс, и через табличный. Достигается это с помощью виртуальных объектов (object views), которых можно так назвать по аналогии с виртуальными таблицами (views).
Для примера вернем описания и наполнение традиционным таблицам схемы SCOTT: EMP и DEPT.
@?/sqlplus/admin/demobld
sqlplus scott/tiger
(Сценарий demobld.sql выводит нас из SQL*Plus).
Упростим для примера описание типа EMPLOYEE_TYP:
ALTER TYPE employee_typ DROP ATTRIBUTE (home);
ALTER TYPE employee_typ ADD ATTRIBUTE (empno NUMBER);
CONNECT scott/tiger
ALTER TYPE employee_typ COMPILE;
Построим таблицу виртуальных объектов типа EMPLOYEE_TYP по исходным данным, хранящимся в EMP:
CREATE VIEW emp_ov OF employee_typ
WITH OBJECT IDENTIFIER (empno) AS
SELECT e.ename, e.hiredate, e.deptno, e.empno FROM emp e;
По своему поведению виртуальные объекты ничем не отличаются от первичных. Проверка (“объектного доступа” к табличным данным):
SELECT e.ename, e.days_at_company () FROM emp_ov e;
SELECT VALUE(e) FROM emp_ov e;
SELECT REF(e) FROM emp_ov e;
UPDATE emp_ov e SET e.ename = INITCAP(e.ename)
WHERE e.empno = 7934;
SELECT ename FROM emp_ov;
Возможность выполнения традиционных DML-операторов над базовыми таблицами, естественно, сохраняется:
UPDATE emp SET ename = UPPER(ename) WHERE empno = 7934;
SELECT ename FROM emp;
Дополнительная информация
За дополнительной информацией обращайтесь в компанию Interface Ltd.
INTERFACE Ltd. |
|