logo
Книга по БД(Вальке А

Informix ds/Universal Data Option - объектно-реляционная субд

Informix DS/Universal Data Option представляет собой реализацию объектно-ориентированной технологии на основе встраивания механизма абстрактных типов данных и механизма наследования в популярный и надежный сервер реляционных баз данных Informix Dynamic Server.

Встраиваемая объектно-ориентированная технология была известна на практике по объектно-реляционной СУБД Illustra (позднее, приобретеннной фирмой Informix и называвшейся Informix Illustra) [Stonebraker 96].

Определение новых базовых типов

Informix Universal Data Option позволяет вводить новые базовые типы данных. При этом можно использовать как встроенные в Informix Dynamic Server методы доступа и хранения, так и определять новые. Рассмотрим способ создания новых базовых типов с использованием встроенных механизмов хранения. Создание базовых типов вместе с алгоритмами хранения и доступа будет рассмотрено ниже.

Необходимость в создании новых базовых типов может возникать во многих случаях. Одним из самых простых случаев - это использование разных метрических систем для одного и того же понятия. Например, если некоторая фирма закупает детали в Америке, то их размеры будут указаны в футах, а цена в долларах, если аналогичные детали закупаются в Германии, то их размеры указываются в метрической системе, а цена в марках.

Если бы речь шла только про размеры, то алгоритм перевода размеров в единую систему измерений четко известен и в базе данных можно было бы хранить только размеры в метрах и сантиметрах. С ценами сложнее - курс обмена зависит от даты конвертации. И если мы будем искать деталь с минимальной ценой, то надо учитывать текущий курс. ОРСУБД Informix DS/Universal Data Option позволяет построить новый базовый тип данных, основанный на существующем, но обеспечивающий автоматическое преобразование к нужному значению. Сами типы вводятся следующими операторами:

CREATE DISTINCT TYPE usd AS MONEY; CREATE DISTINCT TYPE dm AS MONEY;

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

CREATE FUNCTION usd_to_dm(v usd) RETURNS dm; . . . CREATE FUNCTION dm_to_usd(v dm) RETURNS usd; . . .

CREATE IMPLICIT CAST (usd AS dm WITH usd_to_dm); CREATE IMPLICIT CAST (dm AS usd WITH dm_to_usd);

После этого можно сравнивать значения типов usd и dm, полученные из разных таблиц, не вызывая явно функцию преобразования. Такое решение существенно снижает возможность внесения ошибок, связанных с преобразованием значений.

Другой причиной, по которой может возникнуть необходимость во введении нового базового типа данных - это принципиальное отсутствие такого типа. Например, для экспериментальных данных, которые будут храниться в нашей базе данных, недостаточна точность, обеспечиваемая стандартным типом FLOAT. Informix DS/Universal Data Option позволяет ввести новый тип данных FLOAT16, будет использовать для хранения своих значений 16 байт и будет соответствовать нашим требованиям по числу значащих цифр в мантиссе и диапазону порядка:

CREATE OPAQUE TYPE float16 (INTERNALLENGTH=16, ALIGHNMENT=4);

Одного такого оператора недостаточно. Необходимо также задать функции преобразования значений данного вида в текстовый вид (тип данных LVARCHAR) и обратно (это нужно для ввода/вывода значений, экспорта/импорта базы и т.д.). Кроме того, нужно задать дополнительные функции преобразования и сравнения, которые будут использоваться при построении стандартных индексов и при сравнении со значениями других типов:

{обязательные функции преобразования в строку и обратно} CREATE FUNCTION float16_out(float16) RETURNING LVARCHAR . . . .; CREATE FUNCTION float16_in(lvarchar) RETURNING float16 . . . .;

{реализация стандартных операторов ’+’,’-’,’*’,’/’,’>’,’<’ и т.д.} CREATE FUNCTION Plus(float16, float16) RETURNING float16 . . . .; CREATE FUNCTION Plus(float16, float) RETURNING float16 . . . .; . . . . . . .

После того, как все нужные функции определены, можно использовать тип float16 наравне с другими базовыми типами (FLOAT, SMALLFLOAT, INTEGER и т.д.). При этом для хранения, поиска и индексирования используются стандартные механизмы Informix Dynamic Server.

Составные типы данных

Informix DS/Universal Data Option позволяет определять новые составные типы данных. К доступным структурам, которые можно использовать для построения составных типов, относятся:

Запись представляет собой возможность ввести именованные поля. Cтруктура запись структуре record в языке Паскаль и struct в языке C/C++. Тип данных со структурой запись вводится оператором:

CREATE ROW TYPE <имя типа> ( <имя поля> <тип поля>, . . . )

Например:

CREATE ROW TYPE fio_t ( last_name CHAR(40), {фамилия} first_name CHAR(40), {имя} second_name CHAR(40) {отчество} )

Cозданный таким образом составной тип может использоваться наравне и с предопределенными типами для описания колонок в отношении.

CREATE TABLE persons ( tabel_num INTEGER, {табельный номер} fio fio_t, {ФИО} . . . . . . )

Для доступа к отдельным полям внутри типа записи используется традиционный синтаксис - через точку надо указать имя поля:

SELECT tabel_num, fio.last_name, fio.first_name FROM persons WHERE tabel_num = 157

Множество представляет собой неупорядоченное множество значений. В Informix Universal Data Option используется два варианта реализации структуры множества - set и multiset. Первая структура (будем называть ее просто множеством) не допускает повторений элементов внутри себя. Вторая структура (будем называть ее мультимножеством) допускает повторение элементов. Приведем пример, как можно ввести в таблицу persons в качестве атрибута тип данных “дети” со структурой SET:

CREATE TYPE children_t SET ( fio fio_t, wasborn DATE )

CREATE TABLE persons ( tabel_num INTEGER, {табельный номер} fio fio_t, {ФИО} children children_t, {дети} . . . . . )

Список очень похож на мультимножество, но его элементы упорядочены. То есть в списке могут быть повторяющиеся значения, и эти значения можно перебирать по порядку. Рассмотрим реализацию типа со структурой “список” для хранения трудовой биографии (учреждение, дата приема, должность):

CREATE TYPE lab_byog_t LIST ( name CHAR(20), work_from DATETIME YEAR TO DAY, position CHAR(10) )

CREATE TABLE persons ( tabel_num INTEGER, {табельный номер} fio fio_t, {ФИО} children children_t, {дети} lab_byog lab_byog_t, {трудовая биография} . . . . . )

Наследование типов и данных

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

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

CREATE ROW TYPE employee_t ( tabel_num INTEGER, {табельный номер} last_name CHAR(40), {фамилия} first_name CHAR(40), {имя} second_name CHAR(40), {отчество} dolgnost CHAR(20) {должность} base_salary MONEY {оклад} )

В этом типе данных содержится вся информация, которая может быть интересна отделу кадров предприятия. Однако, как мы выяснили выше, для финансового отдела нужны дополнительные сведения. Для продавцов и инженеров дополнительно должна храиться и другая информация, необходимая для расчета зарплаты. Эту информацию можно учесть, создав типы engineer_t и sale_t как наследники типа employee_t:

CREATE ROW TYPE engineer_t ( bonus DECIMAL (5,2) {премия в процентах} ) UNDER TYPE employee_t

CREATE ROW TYPE sale_t ( comission DECIMAL (5,2), {размер комиссионных в %} revenue MONEY {сумма заключенных котрактов} ) UNDER TYPE employee_t

Таким образом, имеется 3 типа данных, два из которых (sales_t и engineer_t) являются наследниками одного (employee_t). Если мы используем эти типы для создания таблиц, мы можем создать иерархию данных:

CREATE TABLE employees OF TYPE employee_t; CREATE TABLE engineers OF TYPE engineer_t UNDER TABLE employees; CREATE TABLE sales OF TYPE sale_t UNDER TABLE employees;

В результате, мы имеем не три разных независимых таблицы, а одну главную таблицу (employees) и две наследованные таблицы (sales и engineers). Внимательный читатель может сказать, что ранее мы критиковали реализацию из трех таблиц как очень неудобную и ненадежную. Но в случае иерархии таблиц правильнее говорить об общей таблице и о двух ее подтаблицах. Каждая запись в наследованной таблице принадлежит и главной тоже (обратное неверно).

Таблица employees

Фамилия

Имя

Отчество

Должность

Оклад

Степанов

Степан

Степанович

админист.

1700

Кузьмин

Кузьма

Кузьмич

админист.

1800

Таблица Sales

Комиссия (%)

Сумма сделок

Иванов

Иван

Иванович

продавец

1000

0.5

500000

Петров

Петр

Петрович

продавец

1000

0.6

400000

Премия (%)

Таблица engineers

Сидоров

Сидор

Сидорович

инженер

1500

20

Матвеев

Матвей

Матвеевич

инженер

1600

20

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

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

SELECT * FROM employees

Фамилия

Имя

Отчество

Должность

Оклад

Степанов

Степан

Степанович

админист.

1700

Кузьмин

Кузьма

Кузьмич

админист.

1800

Иванов

Иван

Иванович

продавец

1000

Петров

Петр

Петрович

продавец

1000

Сидоров

Сидор

Сидорович

инженер

1500

Матвеев

Матвей

Матвеевич

инженер

1600

Выборка данных по таблице engineers даст возможность увидеть всех инженеров, но только их, причем будут доступны и те поля, которые принадлежат типу engineer_t, но не принадлежат типу-родителю employee_t:

SELECT * FROM engineers

Фамилия

Имя

Отчество

Должность

Оклад

Премия (%)

Сидоров

Сидор

Сидорович

инженер

1500

20

Матвеев

Матвей

Матвеевич

инженер

1600

20

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

SELECT * FROM ONLY(employees)

Фамилия

Имя

Отчество

Должность

Оклад

Степанов

Степан

Степанович

админист.

1700

Кузьмин

Кузьма

Кузьмич

админист.

1800

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

CREATE FUNCTION zarplata(p employee_t) RETURNING MONEY; RETURN (p.base_salary);

CREATE FUNCTION zarplata(p engineer_t) RETURNING MONEY; RETURN (p.base_salary + p.base_salary*p.bonus/100);

CREATE FUNCTION zarplata(p sale_t) RETURNING MONEY; RETURN (p.base_salary + p.comission*p.revenue/100);

Теперь можно использовать специальный синтаксис при расчете зарплаты для того, чтобы происходило позднее связывание:

SELECT zarplata(e) FROM employees e;

Специальные методы хранения, поиска и индексации

Informix DS/Universal Data Option позволяет вводить новые базовые типы данных одновременно с введением специальных алгоритмов хранения, доступа и индексирования, отличных от стандартных, реализованных в сервере.

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

{описание нового типа} CREATE OPAQUE TYPE picture ( INTERNALLENGTH=VARIABLE, . . . . . )

{реализация алгоритма записи на диск} CREATE FUNCTION assign(pic picture) RETURNS POINTER EXTERNAL NAME ‘/usr/bin/my_assign()’ LANGUAGE C NOT VARIANT;

Если мы хотим для нового типа ввести специальные алгоритмы индексации и поиска, то надо описать следующие функции:

Далее, надо указать, что вводится новый способ индексирования (pic_tree) и разработанные функции будут использоваться для индексирования созданного типа данных:

CREATE ACCESS_METHOD pic_tree( am_scancost_index = my_am_scancost_index, am_open_index = my_am_open_index , . . . . . )

Далее, использовать новый индекс можно в команде:

CREATE TABLE photos( photo picture, . . . . . ) CREATE INDEX pic1 ON photos(photo) USING pic_tree;

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