Зачем? и Как?

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

Фирма Informix Software объявила о своем намерении выпустить сервер СУБД с поддержкой ООП еще в 1991 году, когда была начата разработка новой архитектуры Informix OnLine Dynamic Server. В декабре 1996 года появился Informix Universal Server, который был сделан на основе архитектуры Informix Dynamic Scalable Architecture и обеспечивал поддержку объектно-ориентированного подхода.

Зачем нужна поддержка объектов в серверах БД?

Самая распространенная модель данных в настоящее время - это реляционная модель. Практически все фирмы-производители систем управления базами данных ориентируются или, хотя бы, поддерживают язык SQL как стандартный язык доступа к реляционным базам данных. Реляционная модель очень хорошо подходит для решения большинства задач. Однако не все задачи могут быть одинаково хорошо и изящно решены с помощью реляционных баз данных вообще и языка SQL в частности. Рассмотрим те ограничения и неудобства, которые заложены в реляционные СУБД.

Неравноправие атрибутов

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

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

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

CREATE TABLE persons (
	tabel_num	INTEGER,  
{табельный номер}
	last_name	CHAR(40), 
{фамилия}
	first_name	CHAR(40), {имя}
	second_name	CHAR(40), 
{отчество}
	department	INTEGER,  
{отдел}
	dolgnost	CHAR(20), {должность}
	emp_from	DATE,     
{работает с...}
. . . . . . 
)

При такой структуре данной таблицы все атрибуты «свалены» в кучу. Более правильно было бы сгруппировать фамилию, имя и отчество в новый составной тип под названием «ФИО», а отдел и должность - в тип «позиция»:

CREATE ROW TYPE fio_t (
	last_name	CHAR(40), 
{фамилия}
	first_name	CHAR(40), {имя}
	second_name	CHAR(40)  
{отчество}
)
CREATE ROW TYPE position_t (
	department	INTEGER,  
{отдел}
	dolgnost	CHAR(20)  {должность}
)

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

CREATE TABLE persons (
	tabel_num	INTEGER,  
{табельный номер}
	fio		fio_t,    {ФИО}
	position	position_t, {позиция}
	emp_from	DATE,     
{работает с...}
	. . . . . . 
)

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

Ограниченный набор базовых типов

В реляционных СУБД набор типов данных, которые можно использовать для атрибутов, фиксирован. Этот набор включает в себя целые числа, числа с плавающей и фиксированной запятой, типы данных для указания времени и даты, а также символьные строки. Подобный набор характерен для финансовых приложений, приложений складского характера и т.д. Современная тенденция такова, что растет число приложений, где необходимо совместить сервер баз данных и мультимедийные типы данных (видео, звук, форматированные тексты). С другой стороны, появляется необходимость обрабатывать новые типы данных, которые не представлены в SQL, а определяются конкретной предметной областью (отпечатки пальцев, фотографии, схемы и т.д.).

Нельзя сказать, что современные реляционные СУБД такой возможности не предоставляют совсем. Например, такие типы данных в Informix Dynamic Server, как BYTE и TEXT предназначены для хранения больших (до 2 ГБт) двоичных и текстовых объектов соответственно. Сервер обеспечивает только хранение таких объектов, он не может сам обрабатывать данные этих типов. Вся обработка должна проводиться на программе-клиенте. То есть для того, чтобы, например, просмотреть все имеющиеся в базе данных отпечатки пальцев и отобрать те, которые похожи на заданный образец, программа-клиент должна последовательно просмотреть все имеющиеся отпечатки. Проведение такого просмотра самим сервером, а, тем более, наличие индекса по отпечаткам, значительно бы ускорило обработку такого запроса.

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

Следовательно, возможность расширения набора базовых типов, то есть возможность разрабатывать новые типы данных и внедрять их в СУБД увеличит эффективность использования СУБД для широкого класса задач. При введении нового базового типа крайне желательно иметь возможность определять для этого типа внутреннее представление (формат), описывать при необходимости новые методы хранения, доступа, индексирования и т.д. При определении нового базового типа для него имеет смысл доопределять те операции, которые уже существуют в языке доступа к данным (например, в SQL). Другими словами, в серверах БД необходимо обеспечить поддержку концепции абстрактных типов данных.

Рассмотрим простейший пример. Предположим, нам надо хранить информацию об адресах и делать выборку по улице. В Российских городах для названия улиц может использоваться дополнительная нумерация (1-я Парковая, 9-я Парковая) или прилагательное «Большой» или «Малый» (Малая Бронная). Соответственно, существует несколько способов записи таких названий. Следующие три названия обозначают одну и туже улицу:

М.Бронная
Бронная, М.
Малая Бронная

Очевидно, что если мы будем использовать для хранения названия улиц обычные строковые типы, то придется следить за тем, чтобы в базе данных названия улиц хранились в едином, унифицированном формате. А при запросе на поиск по названию улицы потребуется преобразовывать введенное пользователем название к этому единому формату. Было бы удобнее, если бы можно было определить новый тип данных «Название улицы», который бы делал это автоматически и, следовательно, минимизировал возможные ошибки. Для данного типа нужно было бы переопределить операцию сравнения, чтобы при сравнении двух значений учитывалось разное написание улиц. Кроме того, для данного типа потребуется переопределить операции сравнения, чтобы сортировка значений данного типа проводилась с учетом разных написаний:

SELECT streets FROM table1 ORDER by streets
. . . .
Бауманская
Б.Бронная
М.Бронная
Буденного
. . . .

Можно привести и другие примеры, когда стандартного набора базовых типов недостаточно. Введение в ту или иную СУБД новых предопределенных типов не может решить данную проблему - всегда найдется задачу, которую не предусмотрели.

Сложные типы данных

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

Таблица «Дети»
ID Фамилия Имя Отчество Родитель
7 Иванов Игорь Иванович 106
8 Иванова Анна Ивановна 106
14 Петров Сергей Петрович 107
. . . . . . . . . . . . . . . ..

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

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

Таблица «Сотрудники»:
ID Фамилия Имя Отчество . . .
106 Иванов Иван Иванович . . . .
107 Петров Петр Петрович . . . .
108 Сидоров Сидор Сидорович . . . .
. . . . . . . . . . . . . . . . .

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

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

CREATE TABLE persons (
	tabel_num	INTEGER,  
{табельный номер}
	last_name	CHAR(40), 
{фамилия}
	first_name	CHAR(40), {имя}
	second_name	CHAR(40), 
{отчество}
	children	SET OF (  
{дети - множество записей вида}
		last_name	CHAR(40), {фамилия ребенка}
		first_name	CHAR(40), {имя ребенка}
		second_name	CHAR(40), {отчество}
		was_born	DATE      {дата рождения}
	)
	. . . . .
)

Мы рассмотрели один конкретный пример, и убедились, что структура данных «множество» в некоторых задачах может быть очень полезна. Можно привести примеры, когда было бы полезно, если бы в качестве атрибутов в таблице можно было использовать сложные, составные типы данных со структурами «массив», «список» и т.д. Реляционные СУБД в своей классической теории не позволяют этого сделать.

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

Современные СУБД практически полностью скрывают от программиста методы физической организации хранения данных (физическую модель). Только администратор сервера базы данных, в силу специфики своих задач, должен и может иметь некоторое представление об используемой физической модели данных.

Существующие реляционные СУБД весьма эффективно реализуют способы представлениея, хранения, доступа и поиска традиционных реляционных данных - чисел, текстовых строк, дат и т.д. Для ускорения поиска обычно используются методы индексирования на основе B-деревьев или B+-деревьев. Когда же мы пытаемся выполнить запрос на поиск текстовой строки, содержащей внутри себя некоторую заданную подстроку, например:

SELECT name, address FROM 
companies WHERE name MATCHES "*Рога*"

или пытаемся выбрать информацию по продажам в феврале в течении нескольких лет:

SELECT price FROM contracts WHERE MONTH(was_signed)=2

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

Существуют специальные методы индексирования для поиска и хранения некоторых типов данных - например, существуют bitmap-индексы, существуют специальные индексы для изображений. Реализовать все возможные типы индексов и все возможные методы хранения на одном сервере БД просто невозможно. Следовательно, надо иметь возможность не просто определять новый тип данных, но и определять методы хранения, доступа и индексирования для данного типа, если стандартные по тем или иным причинам не подходят.

Иерархия данных

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

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

Если мы попытаемся объединить требования отдела кадров и бухгалтерии в единой базе данных, то в стандартных реляционных СУБД мы будем стоять перед выбором: или иметь единую таблицу со всеми возможными атрибутами, или иметь три разных таблицы с разным набором атрибутов.

Фамилия Имя Отчество Должность Оклад Премия (%) Комиссия (%) Сумма сделок
Иванов Иван Иванович продавец 1000   0.5 500000
Петров Петр Петрович продавец 1000   0.6 400000
Сидоров Сидор Сидорович инженер 1500 20    
Матвеев Матвей Матвеевич инженер 1600 20    
Степанов Степан Степанович админист. 1700      
Кузьмин Кузьма Кузьмич админист. 1800      

Фамилия Имя Отчество Должность Оклад Премия (%) Комиссия (%) Сумма сделок
Иванов Иван Иванович продавец 1000 0.5 500000
Петров Петр Петрович продавец 1000   0.6 400000
Сидоров Сидор Сидорович инженер 1500 20    
Матвеев Матвей Матвеевич инженер 1600 20    
Степанов Степан Степанович админист. 1700      
Кузьмин Кузьма Кузьмич админист. 1800      

Таблица «Продавцы»
Фамилия Имя Отчество Должность Оклад Комиссия (%) Сумма сделок
Иванов Иван Иванович продавец 1000 0.5 500000
Петров Петр Петрович продавец 1000 0.6 400000

Таблица «Инженеры»
Фамилия Имя Отчество Должность Оклад Премия (%)
Сидоров Сидор Сидорович инженер 1500 20
Матвеев Матвей Матвеевич инженер 1600 20

Таблица «Администраторы»
Фамилия Имя Отчество Должность Оклад
Степанов Степан Степанович админист. 1700
Кузьмин Кузьма Кузьмич админист. 1800

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

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

Другой подход предполагает наличие трех разных таблиц - своя таблица для каждой категории сотрудников:

Появление трех таблиц вместо одной (в нашем случае, а в реальной жизни число дополнительных таблиц может быть еще больше) усложняет структуру базы данных, делает ее менее прозрачной. Более того, когда в отделе кадров потребуется получить полный список сотрудников вне зависимости от их должности, то в данном запросе потребуется перебрать все три таблицы. При каких-либо организационных изменениях, например, при введении новой категории персонала с повременной оплатой, потребуется завести новую таблицу и произвести нетривиальные измененияы в программах, которые работают сразу с несколькими категориями. Такой подход, одним словом, очень трудоемок и чреват ошибками.

Возможные подходы к реализации

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

Концепция объектно-ориентированного подхода

Концепция объектно-ориентированного подхода возникла в конце 70-х - начале 80-х как альтернатива традиционному стилю программирования. Традиционный стиль программирования подразумевал главенствование алгоритма, программы над данными. Такой подход хорошо подходил для вычислительных задач, относительно неплохо подходил для коммерческих, в основном, учетно-расчетных задач. Как альтернатива традиционному подходу постепенно сформировался объектно-ориентированный подход.

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

Следующим шагом была разработка Б.Страуструпом языка С++, который предназначался для поддержки технологии объектно-ориентированного программирования. Для осуществления такой поддержки в С++ был реализован механизм абстрактных типов данных (с такими свойствами, как полиморфизм и инкапсуляция) и механизм наследования типов. Такой симбиоз получился настолько удачным, что сейчас многие просто отождествляют объектно-ориентированное программирование с поддержкой инкапсуляции, полиморфизма, наследования.

Объектно-ориентированные СУБД

Как утверждается в [Манифест 95], СУБД только тогда может считаться объектно-ориентированной, когда она поддерживает ряд описанных в документе объектно-ориентированных свойств.

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

В модели ODMG-93 [Калиниченко96] были описаны и ЯОД, и ЯМД. К сожалению, пока ни одна из фирм не реализовала полностью этот стандарт. Именно отсутствие реально работающего стандарта является сдерживающим фактором в распространении объектно-ориентированных СУБД.

Объектно-реляционные СУБД

Реляционные СУБД, в отличии от «чистых» объектно-ориентированных СУБД, имеют реально работающие стандарты - стандарты на язык запросов SQL. Кроме того, существует огромная база заказчиков, которые пока не готовы отходить от реляционной технологии. И фирмы-производители реляционных СУБД пошли по пути внедрения объектной технологии в отработанную и популярную технологию реляционных СУБД. В частности, сейчас готовится к выходу стандарт SQL-3, в котором уже заложена поддержка объектно-ориентированной концепции.

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

Технология абстрактных типов данных предполагает:

  • инкапсуляцию (сокрытие деталей реализации внутри типа);
  • полиморфизм (применимость одной операции к разным типам и разный способ вычисления в зависимости от типа);
  • позднее связывание (определение реального типа объекта в момент исполнения);
  • расширяемость (возможность определить новый тип);
  • наследуемость типов (возможность определить новый тип на основе существующего);

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

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

Реализация объектного подхода в Informix

Informix Universal Server - объектно-реляционная СУБД

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

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

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

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

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

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

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 Universal Server позволяет ввести новый тип данных 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 Universal Server.

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

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

  • запись
  • множество
  • список

Запись представляет собой возможность ввести именованные поля. 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 Server используется два варианта реализации структуры множества - 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
Фамилия Имя Отчество Должность Оклад
Степанов Степан Степанович админист. 1700
Кузьмин Кузьма Кузьмич админист. 1800
Иванов Иван Иванович продавец 1000
Петров Петр Петрович продавец 1000
Сидоров Сидор Сидорович инженер 1500
Матвеев Матвей Матвеевич инженер 1600
Фамилия Имя Отчество Должность Оклад Премия (%)
Сидоров Сидор Сидорович инженер 1500 20
Матвеев Матвей Матвеевич инженер 1600 20
Фамилия Имя Отчество Должность Оклад
Степанов Степан Степанович админист. 1700
Кузьмин Кузьма Кузьмич админист. 1800

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

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

SELECT * FROM employees

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

SELECT * FROM engineers

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

SELECT * FROM ONLY(employees)

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

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 Universal Server позволяет вводить новые базовые типы данных одновременно с введением специальных алгоритмов хранения, доступа и индексирования, отличных от стандартных, реализованных в сервере.

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

{описание нового типа}
CREATE OPAQUE TYPE picture (
	INTERNALLENGTH=VARIABLE,
	. . . . . )
{реализация алгоритма записи на диск}
CREATE FUNCTION assign
(pic picture) RETURNS POINTER
	EXTERNAL NAME "/usr/bin/my_assign()"
	LANGUAGE C NOT VARIANT;

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

  • my_am_scancost_index - оценка стоимости использования индекса
  • my_am_open_index - открыть внешний файл с индексом
  • my_am_bedinscan_index - начать просмотр по индексу
  • my_am_getnext_index - взять следующий элемент индекса
  • my_am_endscan_index - закончить просмотр индекса
  • my_am_close_index - закрыть внешний файл с индексом

Далее, надо указать, что вводится новый способ индексирования (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;

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

Заключение

В настоящий момент можно говорить о росте числа задач, для которых хорошо подходит объектно-реляционная технология в СУБД. Informix Universal Server обеспечивает данную технологию за счет реализации механизмов инкапсуляции, полиморфизма и наследования в среду традиционной реляционной СУБД. Можно спорить о достоинствах или недостатках такого подхода, но реальностью является то, что такая технология уже нужна, и она реально существует.

Литература

1. Манифест систем объектно-ориентированных баз данных. М. Аткинсон, Ф. Бансилон, Д. ДеВитт, К. Диттрих, Д. Майер, С. Здоник - Системы Управления Базами Данных, No 4, 1995 стр. 142-155

2. M.Stonebtraker, D.Moore. Object-Relational DBMSs. The Next Great Wave. Morgan Kaufmann Publishers, San Francisco, 1996

3. В.Галатенко, А.Гвоздев. Типы и структуры данных в Informix Universal Server. Jet Info/Информационный бюллетень, N12-13, 1997 г.

А. Грачев
Informix Software, Москва