Замечания обозревателя:
Уважаемые читатели! Вашему вниманию предлагается не самая свежая статья, посвященная принципам организации современных вариантов DB2. Она была опубликована в конце 1996 г. в журнале DB2 Online Magazine (www.db2mag.com). По слухам, официальный полный перевод этой статьи доступен в бумажной форме в Московском представительстве компании IBM. Тем не менее, мне показалось полезным опубликовать обзор статьи на нашем сервере по следующим причинам.

Во-первых, имя автора этой статьи, господина Дональда Чемберлина известно отечественным специалистам в области баз данных более 20 лет. Для меня начало деятельности Дона в компании IBM связано с легендарным проектом System R, реализация которого впервые продемонстрировала практические возможности реляционного подхода к базам данных. В настоящее время господин Чемберлин работает в научно-исследовательской лаборатории IBM (www.ibm.almaden.com) и является лидером группы, связанной с перспективными архитектурами систем баз данных.

Во-вторых, представляемая вашему вниманию статья, на мой взгляд, исключительно просто и понятно описывает основные принципы объектно-реляционного подхода в связи с его реализацией в DB2 for Common Servers, которая, в свою очередь, является одной из основных составляющих IBM DB2 Universal Database.

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

С уважением, Сергей Кузнецов

DB2 Online Magazine, Winter 1996
Anatomy of an Object-Relational Database

Donald D. Chamberlin

(http://www.db2mag.com/9601cha.htm)

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

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

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

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

Обе указанные возможности позволяют повысить ценность хранимых данных за счет расширения их семантического содержимого. Тенденция к повышению роли семантического содержимого хранимых данных является наиболее важной в современном управлении базами данных. В соответствии с этой тенденцией реляционные системы баз данных расширяются в двух направлениях: (1) добавлении "объектной инфраструктуры" к самой системе баз данных в виде поддержки определяемых пользователями типов данных, функций и правил; (2) построении поверх этой инфраструктуры "реляционных расширителей", которые поддерживают специализированные приложения, такие как выборка изображений, развитый текстовый поиск, географические приложения. Система, которая обеспечивает объектную инфраструктуру и набор реляционных расширителей, называется "объектно-реляционной".

Объектно-реляционные системы объединяют достоинства современных объектно-ориентированных языков программирования с такими свойствами реляционных систем как множественные представления данных и высокоуровневые непроцедурные языки запросов. В данной статье анализируется объектная инфраструктура и реляционные расширители, поддерживаемые современной объектно-реляционной системой IBM DB2 for Common Servers, которая в дальнейшем тексте будет называться просто DB2.

Объектная инфраструктура

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

В DB2 поддерживаются три типа данных для хранения больших объектов: BLOB для бинарных объектов, CLOB для символьных строк и DBCLOB для строк, в которых используются двухбайтовые наборы символов. Для каждого из этих типов данных можно хранить объекты объемом до 2 Гбт. Когда большие объекты сохраняется в столбце таблицы, то на самом деле столбец содержит "дескриптор" каждого такого значения; сами же большие объекты хранятся вне таблицы. Такой подход предотвращает влияние наличия больших объектов на физическую кластеризацию таблицы, которая может уменьшить число чтений страниц внешней памяти при просмотре таблицы. Опции оператора CREATE TABLE позволяют управлять расположением больших объектов на физическом носителе.

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

По причине большого размера крайне желательно минимизировать число перемещений и копирований больших объектов. В среде DB2 прикладная программа может объявить переменную-"локатор", которая представляет значение большого объекта, но реально его не содержит. Содержимое локатора является спецификацией того, как значение большого объекта может быть материализовано в случае необходимости. Локатор может быть использован для представления значения большого объекта в любом выражении SQL, и операции над локаторами очень эффективны, поскольку при их выполнении происходит работа с "предписаниями" по материализации, а не с сами значениями больших объектов. Например, если LOC1 и LOC2 являются переменными-локаторами, то при вычислении выражения LOC1 || LOC2 выполняется конкатенация спецификаций, содержащихся в этих переменных, а не самих значений. При использовании локаторов прикладная программа может выполнить серию действий над значением большого объекта, откладывая его материализацию до последнего момента.

Часто требуется импортировать большой объект из файла в базу данных или экспортировать большой объект из базы данных в файл. DB2 дает возможность прикладным программам обмениваться значениями больших объектов между базой данных и файлом без перемещения значений через буфера программы. В программе может быть объявлена переменная "ссылка на файл", которая содержит имя нужного файла. Ссылка на файл может использоваться в операторах SQL как входная или выходная переменная, представляющая содержимое файла, которое интерпретируется как большой объект. Совместно локаторы и ссылки на файл часто дают возможность обработки больших объектов без их реального считывания в память программы.

Определяемые пользователями функции

DB2 обеспечивает более 100 встроенных функций для выполнения различных вычислений над числами, строками, датами и другими типами данных, а также дает пользователям возможность создания собственных функций с использованием языков Си, Си++ и Бейзик. Определяемые пользователями функции (User-Defined Functions - UDF) могут принимать параметры и могут быть использованы в любом выражении SQL, где предполагается наличие скалярного значения.

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

Комбинация имени функции и типов ее параметров называется "сигнатурой" функции. SQL позволяет "перегружать" имя функции, т.е. определять несколько функций с одним именем и разными типами параметров. При обработке вызова функции DB2 вызывает функцию, типы параметров которой строго соответствуют типам аргументов вызова.

Определяемые пользователями типы

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

Следующие операторы создают два индивидуальных типа с именами DOLLARS и YEN, базирующихся на встроенном типе Decimal. Фраза WITH COMPARISONS означает что можно сравнить любые два значения типа DOLLARS и любые два значения типа YEN, однако значение типа DOLLARS не может быть сравнено со значением типа YEN или с обычным десятичным значением.


   CREATE DISTINCT TYPE DOLLARS AS DECIMAL (10,2) WITH COMPARISONS;



   CREATE DISTINCT TYPE YEN AS DECIMAL (10,2) WITH COMPARISONS;

При создании индивидуального типа DB2 генерирует функцию, преобразующие значение индивидуального типа в значение его базового типа и наоборот. Например, при создании типа DOLLARS создаются функции преобразования DOLLARS(DECIMAL) со значением типа DOLLARS и DECIMAL(DOLLARS) со значением типа DECIMAL(10,2).

Сразу после создания индивидуального типа единственными операторами, применимыми к его значениям, являются операторы сравнения. Например, если SALARY и BONUS - это два столбца типа DOLLARS, то SALARY=BONUS и SALARY>BONUS являются допустимыми предикатами, но выражения SALARY+BONUS и SALARY*BONUS не допускаются, поскольку для типа DOLLARS не определены арифметические операции.

Легко указать, какие из операций базового типа являются осмысленными для созданного на его основе индивидуального типа. Каждый встроенный оператор, такой как "+", реализуется функций с тем же именем, что и оператор. Чтобы сделать этот оператор применимым к индивидуальному типу, нужно просто создать функцию с тем же именем, что и оператор, принимающую параметры и/или возвращающую результат индивидуального типа данных. Функция, реализующая оператор, может основываться на функции, реализующей встроенный оператор. В следующих предложениях SQL определяются оператор "+" для двух значений типа DOLLARS и оператор "*" для значения целого типа и значения типа DOLLARS:


   CREATE FUNCTION "+" (DOLLARS,DOLLARS)

   RETURN DOLLARS

   SOURCE

       SYSIBM."+" (DECIMAL(),DECIMAL());



   CREATE FUNCTION "*" (INTEGER,DOLLARS)

   RETURN DOLLARS

   SOURCE

       SYSIBM."*" (INTEGER,DECIMAL());

После выполнения этих предложений выражения SALARY+BONUS и 2*SALARY будут допустимыми, но выражение SALARY*BUNUS останется недопустимым, поскольку оператор умножения не определен для двух значений типа DOLLARS.

Конечно, может потребоваться расширить функциональность индивидуального типа за пределы набора операторов базового типа. Например, можно создать тип ADDRESS, основанный на встроенном типе VARCHAR(50). После этого можно создать определяемую пользователями функцию TIMEZONE(ADDRESS), вычисляющую временную зону этого адреса. Как и любая другая UDF, функция TIMEZONE может быть написана на языках Си, Си++ и Бейзик и должна быть зарегистрирована в соответствующей базе данных.

Активные данные

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

В DB2 существуют две категории активных данных - ограничения и триггеры. Ограничения являются декларативными утверждениями, истинность которых контролируется системой, например, "Размер обуви всегда представляется положительными числами" или "У каждого служащего имеется менеджер". Триггеры - это автоматические действия, которые срабатывают каждый раз при возникновении определенного события или условия, например, "Когда останется только 10 карандашей, нужно заказать еще 100" или "Следует вести учет всех тех, кто изменяет содержимое таблицы ACCOUNTS".

В DB2 поддерживаются следующие типы ограничений:

  1. Ограничения NOT NULLS, запрещающие неопределенным значениям появляться в указанном столбце
  2. Ограничения UNIQUE, запрещающие наличие значений-дубликатов в указанном столбце или группе столбцов
  3. Ограничения PRIMARY KEY, специфицирующее указанный столбец или группу столбцов как одновременно обладающие свойствами UNIQUE и NOT NULL
  4. Ограничения CHECK - предикаты, такие как BONUS Раздел WITH CHECK OPTION определения представлений, запрещающий занесение или удаление данных через представление, если это противоречит определению представлению
  5. Ограничения FOREIGN KEY (называемые также ограничениям "ссылочной целостности"), устанавливающие контролируемую системой связь между двумя таблицами, "таблицей-предком" и "таблицей-потомком". Для каждого отличного от неопределенного значения внешнего ключа должно иметься совпадающее с ним значение ключа таблицы-предка.

Ограничения представляют собой декларативные правила. Триггер больше похож на "джина", который просыпается и выполняет приказы при возникновении определенных событий. Вот некоторые из возможностей механизма триггеров DB2:

  1. Триггер может быть активизирован при выполнении операций занесения, удаления или модификации строк указанной таблицы или при модификации определенных столбцов таблицы.
  2. Можно потребовать срабатывания триггера до или после обработки события, которое его активизирует.
  3. Триггер может срабатывать в точности один раз при активизации его оператором SQL или же вызываться для каждой строки, изменяемой оператором SQL.
  4. При активизации триггер может вычислять предикат, называемый "условием триггера". Тогда тело триггера выполняется только если его условие истинно.
  5. Тело триггера может состоять из одного или нескольких операторов SQL. В этих операторах могут использоваться специальные переменные, указывающие на значения строки или группы строк до и после активизации триггера. Если в тело триггера входят операторы модификации базы данных, то авторизация доступа производится от имени создателя триггера, а не того пользователя, оператор которого активизировал триггер. Это позволяет создателю триггера "инкапсулировать" некоторые привилегии в формы, доступные менее привилегированным пользователям.

Рассмотрим, например, каким образом триггер может автоматически поддерживать столбец данных. Предположим, что база данных содержит таблицу STOCKS со столбцами SYMBOL, PRICE и HIGHPRICE. Текущая цена всегда поддерживается в столбце PRICE. Можно захотеть, чтобы при изменении текущей цены в столбце HIGHPRICE всегда оказывалось ее максимальное значение. Этого можно достичь путем создания следующего триггера:


   CREATE TRIGGER stockhigh

   NO CASCADE BEFORE UPDATE ON stocks

   REFERENCING NEW AS newrow

   FOR EACH ROW MODE DB2SQL

   WHEN (newrow.highprice IS NULL OR

   newrow.price > newrow.highprice)

   SET newrow.highprice = newrow.price;

Синергия

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

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


   CREATE DISTINCT TYPE POLYGON AS

   BLOB(1M);

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

После создания индивидуального типа желательное поведение многоугольников может быть указано путем создания набора соответствующих UDF. По крайней мере одна из этих функций должна быть "конструктором", создающим многоугольник на основе более простых типов, таких как POINT или DOUBLE. Работа функции-конструктора состоит в упаковке примитивных частей многоугольника в BLOB с последующим преобразованием типа BLOB к типу POLYGON (для этого следует использовать сгенерированную системой функцию преобразования типов POLYGON(BLOB).

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


   degree(Polygon) returns Integer;



   area(Polygon) returns Double;



   perimeter(Polygon) returns Double;



   rotate(Polygon, Double) returns Polygon;



   intersect(Polygon, Polygon) returns Polygon;

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


   CREATE TABLE properties

   (taxid      Char(6) PRIMARY KEY,

    owner      Varchar(32),

    assessment Dollars,

    parcel     Polygon);

Для нахождения собственников больших участков можно использовать следующий запрос на языке SQL:


   SELECT owner, area(parcel)

   FROM   properties

   WHERE  area(parcel) > 20000;

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


   ALTER TABLE properties

   ADD COLUMN  area Double;

Теперь определим триггеры, которые активируются при выполнении над таблицей PROPERTIES операторов INSERT и UPDATE. Вот как могло бы выглядеть определение триггера для INSERT:


   CREATE TRIGGER insertprop

       NO CASCADE

       BEFORE INSERT ON properties

       REFERENCING NEW AS newrow

       FOR EACH ROW MODE DB2SQL

       SET newrow.area =

       area(newrow.area);

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


   CREATE INDEX proparea ON

   properties(area);

Теперь перепишем запрос в форме, которая даст возможность DB2 использовать этот индекс:


   SELECT owner, area

   FROM   properties

   WHERE  area > 20000;

Реляционные расширители

На основе представленной выше объектной инфраструктуры могут создаваться реляционные расширители для поддержки конкретных прикладных областей. Далее будут обсуждены свойства одного из таких расширителей - Text Extender, а также приведена краткая характеристика других расширителей, существовавших для DB2 к моменту написания статьи.

Text Extender. Этот расширитель поддерживает быстрый контекстный поиск в больших текстовых документах. Не требуется, чтобы документы хранились в специальном формате; могут быть использованы существующие документы во многих популярных форматах, включая Microsoft Word, Word Perfect и AmiPro. Для использования Text Extender документы должны быть загружены в столбец таблицы DB2 с применением типа данных символьных строк (например, CLOB). Расширитель создает специального рода индекса на хранимых документах и обеспечивает набор функций, использующих этот индекс для поиска документов, содержащих желаемые комбинации слов и фраз.

Поскольку документы используемые с применение Text Extender хранятся в столбце таблицы DB2, в запросе могут комбинироваться условия, основанные на содержании документа, и условия, накладываемые на другие столбцы данных. Например, таблица журнальных статей может включать столбцы, содержащие название журнала, дату публикации, название и полный текст каждой статьи. Можно сформулировать запрос по поводу статей, напечатанных в Newsweek в 1990 г. и содержащих слова "Iraq" и "embargo" в одном параграфе.

Подобно всем UDF, функции, реализованные в Text Extender, могут использоваться в обычных операторах SQL. Одной из наиболее важных таких функций является функция CONTAINS, возвращающая значение 1, если данный документ соответствует заданному шаблону поиска. Шаблон поиска может содержать несколько фраз, соединенных операциями "&" (и), "|" (или) и NOT. В шаблоне можно также указать, что определенные слова или фразы должны встречаться в одном предложении или параграфе. Например, следующий запрос предназначен для поиска статей, содержащих слова "cooking" и либо "Chinese" либо "Japanese" в любом порядке, но не содержащих слово "sishi":


   SELECT magazine, date, title

   FROM   articles

   WHERE  CONTAINS(articletext,

       '("cooking"

        & ("Chinese" | "Japanese")

        & NOT "sushi")') = 1;

Другие расширители

  1. Image Extender может хранить и выбирать изображения, представленные в нескольких популярных форматах, включая GIF, JPEG и BMP, а также преобразовывать изображения из одного формата к другому.
  2. Video Extender может хранить и выбирать видео-последовательности, представленные в нескольких популярных форматах, включая MPEG1, AVI и Quicktime.
  3. Audio Extender может хранить и выбирать аудио-клипы в нескольких популярных форматах, включая AIFF, MIDI и WAVE.
  4. Fingerprint Extender может хранить и выбирать отпечатки пальцев, представленные в специальном формате, а также производить поиск отпечатков пальцев по заданному образцу.

Размышления о будущем

Все возможности, описанные в этой статье были доступны в DB2 for Common Servers с июля 1995 г. IBM планирует распространить те же возможности и на других членов семейства DB2. Особенно важно то, что через год после выпуска компанией IBM ее первой объектно-реляционной систем, в лабораториях IBM производилась интенсивная разработка дополнительных объектно-реляционных средств, включая следующее: