Базы данных: Oracle


Моделирование групп объектов в Oracle



  Автор: Владимир Пржиялковский

  Источник: ,

Введение

Объектный подход к моделированию БД, безусловно, имеет свою притягательность, хотя, как замечалось в предыдущей статье () ценность его преувеличивать не стоит, так как и он не лишен собственных проблем и ограничений (к слову сказать, известных задолго до середины 90-х годов, когда большая промышленность СУБД им вооружиться). Однако подход есть, и как-то решать задачу моделирования он обязан. В предыдущей статье говорилось о том, как в Oracle можно создавать и хранить отдельные объекты. В жизни этого недостаточно, и требуется иметь возможность моделировать группы объектов: наборы адресов, списки сотрудников и т. д.

Такая возможность предусматривалась, например, в сетевой модели данных, исторически предшествовавшей реляционной, и проектировалась в виде расширения реляционной создателем последней Э. Коддом (в силу ряда причин это расширение было проигнорировано разработчиками промышленных "реляционных" СУБД). Здесь будут рассмотрены возможности моделирования групп объектов, реализованные в Oracle последних версий (8, 9).

Таблицы хранимых и синтезированных объектов

Первая возможность моделирования групп из объектов в Oracle известна по предыдущей статье: это таблицы "исконных" объектов (object tables) и таблицы "виртуальных", или "синтезированных" объектов (object views). Исконные объекты хранятся как самостоятельные сущности в БД, а синтезированные дают только видимость объектов (по потребительским свойствам почти не отличимую от истинных объектов) на основе данных, хранимых в обычных или объектных таблицах.

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

DROP TYPE employee_typ FORCE;
DROP TABLE e_moscow;
DROP TABLE e_leningrad;
DROP TABLE employment;

CREATE TYPE employee_typ AS OBJECT (
    ename    VARCHAR2(50),
    job      VARCHAR2(10))
/

CREATE TABLE e_moscow OF employee_typ;

CREATE TABLE e_leningrad OF employee_typ;

INSERT INTO e_moscow VALUES (
    'Scott',
    'Manager');
...

INSERT INTO e_leningrad VALUES (
    'Smith',
    'Salesman');
...

CREATE TABLE employment (
       dname VARCHAR2(50),
        employee REF employee_typ);

INSERT INTO employment VALUES (
       'Operations',
       (SELECT REF(m) FROM e_moscow m WHERE m.ename = 'Scott'));
...

Этот способ, однако, не лишен своих ограничений. Например, по данным таблицы EMPLOYMENT нельзя понять, проживает ли сотрудник в Москве или Ленинграде. Нельзя переселить сотрудника из Москвы в Ленинград (можно только удалить его из одной таблицы и создать в другой объект с теми же атрибутами) и так далее.

Коллекции

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

В Oracle они могут быть двух видов: вложенные таблицы и массивы типа VARRAY.

Вложенные таблицы

Термин, выбранный для этого вида коллекций кажется не совсем удачным. Речь на самом деле идет о моделировании не таблиц, а списков. В отличие от предыдущего примера построим таблицу DEPARTMENTS с перечнем отделов, причем в строке о каждом отделе будем хранить список сотрудников. Предварительно, однако, нужно будет создать тип для такого поля-списка:

CREATE TYPE employee_nlist_typ AS TABLE OF employee_typ
/

CREATE TABLE department (
       dname            VARCHAR2(20),
       emps             employee_nlist_typ)
NESTED TABLE emps STORE AS emps_nt_tab;

Фраза NESTED TABLE - чисто техническая. Она обязана тому, что физически Oracle будет хранить в поле EMPS не список объектов-сотрудников, а список их системных идентификаторов, присвоенных при помещении самих сотрудников в специально создаваемую для таблицы DEPARTMENT служебную таблицу. Таким образом при использовании вложенных таблиц значения элементов хранимых списков физически всегда хранятся в специальной отдельной таблице, которой мы обязаны придумать название. В таком решении есть своя логика, так как для этой специальной таблицы мы можем указать собственные характеристики хранения и некоторые другие вещи.

Вот как можно заполнить таблицу отделов:

INSERT INTO department VALUES (
    'OPERATIONS',
    employee_nlist_typ (
       employee_typ ('Scott', 'Manager'),
       employee_typ ('Smith', 'Salesman')
       )
    );

Вотделе Operations теперь два сотрудника:
COLUMN emps FORMAT A60 WORD
SELECT * FROM department;


По терминологии предыдущей статьи сотрудники в таблице DEPARTMENT - "объектные атрибуты". Другой способ смоделировать ситуацию "сотрудники-отделы" с помощью коллекции - воспользоваться списком ссылок на сотрудников, реально существующих в собственных таблицах.

Работа в PL/SQL

Вот как можно работать со вложенными таблицами в PL/SQL:

DECLARE
    ee employee_nlist_typ;
BEGIN
    SELECT emps INTO ee FROM department WHERE dname = 'Operations';
    DBMS_OUTPUT.PUT_LINE(ee(1).ename);
    DBMS_OUTPUT.PUT_LINE(ee(2).ename);
END;
/

В этом примере для упрощения использованы предпосылки о том, что (а) отдел с названием 'Operations' всего один и (в) сотрудников в нем - [по крайней мере] двое.

Массивы типа VARRAY

Массивы типа VARRAY потребительски во многом похожи на вложенные таблицы, но имеют и ряд существенных технических и внешних отличий. Например, они обязаны иметь ограничение на максимальное число элементов в конкретных массивах, наподобие типу VARCHAR2. Еще они не требуют для хранения данных служебной таблицы, наподобие вложенной таблицы. Есть и другие отличия. Фирма Oracle советует использовать вложенные таблицы, если нужно хранить неупорядоченные списки и VARRAY, если нужно хранить упорядоченные.

Пример использования для группировки сотрудников коллекции VARRAY может выглядеть так:

CREATE TYPE employee_vlist_typ AS VARRAY(20) OF employee_typ
/

CREATE TABLE department1 (
        dname             VARCHAR2(15),
        emps              employee_vlist_typ
        );

Этим типом мы запретили отделам иметь более 20-и сотрудников.

Добавление нового отдела делается как и для вложенных таблиц:

INSERT INTO department1 VALUES (
    'Operations',
    employee_vlist_typ (
       employee_typ ('Scott', 'Manager'),
       employee_typ ('Smith', 'Salesman')
       )
    );

Приведенный выше код на PL/SQL для массива сотрудников VARRAY проработает так же.

Преобразования коллекций

Как и следовало бы ожидать от СУБД Oracle, плотная, а не поверхностная работа с коллекциями в качестве средства моделирования групп объектов требует знания большого числа "деталей". Здесь не место разъяснять их все, но одну важную для коллекций возможность стоит привести. Имеется в виду разворачивание коллекции в список строк, столь привычный для традиционной работы.

Для того, чтобы посмотреть список сотрудников отдела 'Operations' в более привычном виде, следует воспользоваться специальной функцией TABLE:

SELECT * FROM TABLE(SELECT emps FROM department);

К аргументу функции TABLE (это вложенный SELECT) есть одна настоятельная просьба: возвращать одну и только коллекцию. Наши данные это обеспечивают, а иначе вложенный SELECT нужно было бы подправить.

Аналогичный пример для массива VARRAY:

SELECT ename FROM TABLE(SELECT emps FROM department1);

Замечательно, что это преобразование решает задачу и изменения списка средствами SQL:

INSERT INTO TABLE(SELECT emps FROM department)
VALUES ('Allen', 'Salesman');

SELECT * FROM TABLE(SELECT emps FROM department);

(Эта возможность не сработает, однако, для массива VARRAY, который в БД ведет себя, по сути, как скаляр, допуская изменение поля-списка как единого, уже сформированного целого). Если бы возможность такого преобразования отсутствовала, добавить сотрудника в отдел или удалить можно было бы только программным способом, проще всего в PL/SQL.

Естественно, никто не мешает осуществить и массовую вставку:

INSERT INTO TABLE(SELECT emps FROM department)
SELECT ename, job FROM emp;

Для обратного преобразования, из таблицы в коллекцию, потребуется более сложная конструкция:

SELECT
   CAST (MULTISET(SELECT ename, job FROM emp) AS employee_nlist_typ)
FROM DUAL;

Однако такое преобразование на практике менее востребовано.







При перепечатке любого материала с сайта, видимая ссылка на источник www.warayg.narod.ru и все имена, ссылки авторов обязательны.

© 2005
 

Hosted by uCoz