Точка Зрения на ОРСУБД

Сергей Савушкин, 2003, 2012

 

Статья посвящена прежде всего логическому представлению объектов в базе данных посредством двух объектно-реляционных языков: SQL3 (или SQL:1999) (http://www.objs.com/x3h7/sql3.htm) и Zigzag (http://savtechno.com). Реально SQL3 не является окончательным и не поддерживается полностью всеми известными объектно-реляционными системами управления базами данных. Речь в статье идет об объектно-ориентированном языке SQL систем Oracle и Informix. Zigzag отличается от SQL не только в синтаксисе, но также и в механизмах обработки данных. Принимая во внимание что SQL - язык исчисления, Zigzag - язык  объектно-реляционной алгебры.

 

Чтобы определить Объектно-Реляционную Систему Управления Базами Данных (ОРСУБД) достаточно воспользоваться простым уравнением: ОРСУБД = ОСУБД + РСУБД = (O + Р) * СУ * БД. На логическом уровне ОРСУБД есть методы обработки СУ применяемые к структуре данных БД, которая характеризуется понятиями О объектная и Р реляционная.

 

Все необходимое для объектного представления доступно в объектной СУБД (ОСУБД). Обычно ОСУБД приравнивают к OОСУБД, а именно к СУБД интегрированной с Объектно-Ориентированным (OO) языком программирования как C++ и Java. Характерные свойства OОСУБД - 1) комплексные данные, 2) наследование типа, и 3) объектное поведение. Комплексные данные могут быть реализованы через постоянные объекты (persistent objects) и XML. OO языки программирования с их определением класса формируют наследование и объектное поведение.

 

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

Комплексные данные

Создание комплексных данных в большинстве существующих SQL ОРСУБД основано на предварительном определении схемы через определяемый пользователем тип (UDT - user-defined type). Таблица остается самой наглядной формой представления комплексных данных в любой ОРСУБД.

 

students

id

name

course

first

last

st031

Jane

Hunter

Economy

Planning

st072

Richard

White

Computers in Engineering

 

Особенности приведенной выше таблицы следующие: Атрибут (или поле, или столбец) "name" состоит из "first" и "last" атрибутов; Одним из значений атрибута "course" является множество из элементов "Economy" и "Planning". Такая структура возможна, например, для IBM Informix SQL (http://publib.boulder.ibm.com/infocenter/idshelp/v111/index.jsp?topic=/com.ibm.sqls.doc/sqls.htm).

 

CREATE ROW TYPE Student (

  id CHAR(5),

  name ROW (first VARCHAR(12), last VARCHAR(20)),

  course SET (VARCHAR(128) NOT NULL)

);

CREATE TABLE students OF TYPE Student;

INSERT INTO students

VALUES (

  'st031',

  ROW('Jane', 'Hunter'),

  SET('Economy', 'Planning')

);

INSERT INTO students

VALUES (

  'st072',

  ROW('Richard', 'White'),

  SET('Computers in Engineering')

);

 

Другие SQL ОРСУБД предлагают другие конструкторы составных типов, например VARRAY или ARRAY вместо SET, и OBJECT вместо ROW. Конструкторы простых встроенных типов с ограничениями как CHAR(5) унаследованы от SQL2.

 

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

 

Zigzag принимает вышеупомянутую таблицу без предварительного описания схемы, как ниже, через процедуру readTable (http://www.savtechno.com/docz/zigzag/z/zlanguage.html). Первая строка входных данных объявляет названия атрибутов. Имя "id" может быть опущено, а "student" обозначать и первичный ключевой атрибут и таблицу.

 

$readTable() <

student; name:first; name:last; course

st031  ; Jane      ; Hunter   ; Economy, Planning

st072  ; Richard   ; White    ; Computers in Engineering

>;

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

Иерархия структурных комплексных данных предполагает иерархию типов и их наследование, когда все характеристики (атрибуты) типа передаются его подтипам. Рассмотрим следующие данные:

employees

Name

Salary

Sylvia Karsen

30000.00

 

programmers

Name

Salary

Language

Project

William Helprin

40000.00

C++

Seestorm

 

representatives

Name

Salary

Region

Akiko Yokomoto

50000.00

Asia

Типы данных формируют иерархию изображенную ниже.

               Employee(Name,Salary)
              /                     \
Programmer(Language,Project)   Representative(Region)

SQL представление

Для создания базы данных воспользуемся преимуществами Oracle SQL (http://docs.oracle.com/ ).

CREATE TYPE Employee AS OBJECT (
  Name      VARCHAR2(20),
  Salary    NUMBER(8,2)
) NOT FINAL;
CREATE TYPE Programmer UNDER Employee (
  Language   VARCHAR2(12),
  Project    VARCHAR2(30)
);
CREATE TYPE Representative UNDER Employee (
  Region    VARCHAR2(30)
);
CREATE TABLE employees OF Employee;
CREATE TABLE programmers OF Programmer;
CREATE TABLE representatives OF Representative;
INSERT INTO employees
  VALUES (Employee('Sylvia Karsen', 30000.00));
INSERT INTO programmers
  VALUES (Programmer('William Helprin', 40000.00, 'C++', 'Seestorm'));
INSERT INTO representatives
  VALUES (Representative('Akiko Yokomoto', 50000.00, 'Asia'));

 

Подтипы "Programmer" и "Representative" наследуют все атрибуты от супертипа "Employee". Тем не менее, запрос об объектах "employees" типа "Employee" в Oracle не означает запрос об объектах подтипов, а именно "programmers" и "representatives".

 

SELECT e.Name

  FROM employees e;

Результатом SQL запроса будет:

 

Name

--------------------

Sylvia Karsen


Zigzag представление

 

Та же иерархия данных может быть выражена в Zigzag. Отдельное определение типов не требуется.

 

$readTable() <

Employee; Name         ; Salary

#       ; Sylvia Karsen; 30000.00

>;

$readTable() <

Employee:Programmer; Name           ; Salary  ; Language; Project

#                  ; William Helprin; 40000.00; C++     ; Seestorm

>;

$readTable() <

Employee:Representative; Name          ; Salary  ; Region

#                      ; Akiko Yokomoto; 50000.00; Asia

>;

 

Zigzag запрос об объектах типа "Employee" означает также запрос об объектах его подтипов "Programmer" и "Representative".

 

= Name:(Employee:);

 

В SQL в результате мы получаем только одного служащего Sylvia Karsen, заданного явно. В Zigzag, на данный запрос у нас получается три значения. Все они реально являются служащими не так ли? :). Результат:

 

Name: Sylvia Karsen, Akiko Yokomoto, William Helprin

 

Отличительное свойство Zigzag - то, что тип действительно является объектом, который определяет класс (в смысле множества) других объектов. Другими словами, типы в Zigzag – также данные. Кроме того, наследование типа означает не только наследование имен атрибутов, но также и наследование значений атрибутов. Например, предположим, что все программисты расположены в одном отделе "E". В случае SQL3 требуется вставить значение "E" во все строки таблицы "programmers". Относительно Zigzag, мы можем быть удовлетворены, если установим "E" только для объекта "Programmer".

 

$readTable() <

Employee  ; Department

Programmer; E

>;

 

Чтобы проверить наследование значения "E", введем следующий Zigzag запрос, переводимый как "имена работников отдела Е".

 

= Name:(Employee:(Department:E));

 

Повторюсь, здесь речь идет о наследовании значения атрибута (не имени атрибута). И значение это наследуется Programmer, то есть тем, что в SQL3 называется типом :). Результат будет такой:

 

Name:William Helprin

Объектное поведение

Чтобы дать разработчику максимальные компьютерные возможности, СУБД должна обеспечить некоторую связь (например, ODBC) внешнего языка программирования с внутренним языком СУБД. Дополнительным преимуществом, преобразовывающим (Р)СУБД в О(Р)СУБД, является доступ из внутреннего языка СУБД к программным объектам, а именно к объектам внешнего/внутреннего языка объектно-ориентированного программирования (ООП). Такие программные объекты должны быть сохраняемыми и переносимыми для обработки в базе данных, поэтому они называются обычно как постоянно существующие (или долговременные) объекты. Внутри базы данных все отношения с постоянным программным объектом есть отношения с его объектным идентификатором (OID). Отображение внешних постоянных программных объектов в базе данных - не проблема (смотрите, например, JDO/JPA инструменты http://db.apache.org/jdo/why_jdo.html / http://www.oracle.com/technetwork/java/javaee/tech/persistence-jsp-140049.html).

 

Ограниченный реальный объект можно описать через традиционный элемент базы данных, запись или поле. Этот элемент называется объектом данных (или объектом базы данных). Ранее описанные комплексные элементы данных также - объекты данных. Чем отличается программный объект? Каждый программный объект формально может быть определен как экземпляр (или образец), полученный из отображения типа:

 

          тип       = { атрибуты,  методы    }

           ↓             

ОБЪЕКТ =  экземпляр = { состояние, поведение }

 

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

 

object.method(parameters) = function(object,parameters);

или

object.method(parameters) = procedure(object,parameters);

 

Левые выражения позволяют воспринять метод как составляющую, которая может быть выражена через объекты. Рассмотрим свойства, связанные с методом и реализованные в ООП языках (см. например  http://home.cogeco.ca/~ve3ll/jatutor0.htm или http://java.sun.com/). Известно, что поведение объекта (а значит метод), обозначенное одним названием, может отличаться в соответствии с типом объекта или ситуации (параметров). ООП языки учитывают это как принцип полиморфизма, который кратко описывается через выражение "один интерфейс, много обеспечений". Другие принципы ООП наследование и инкапсуляция связаны как с методами, так и с атрибутами. Наследование было уже рассмотрено. Принцип инкапсуляции в ОРСУБД имеет иной оттенок. Даже продвинутые Oracle 9i и IBM DB2 V8 не учитывают степень инкапсуляции через PUBLIC, PRIVATE и PROTECTED как Java или С++. Инкапсуляция в ОРСУБД сводится к замене атрибута на метод, то есть к использованию виртуальных атрибутов. Например, вместо атрибута price может быть использован метод getPrice(). В последующем обсуждении принципы ООП демонстрируются через Oracle SQL и Java/Zigzag.

SQL представление

 

Ниже, SQL пример создает таблицу "equipments" абстрактного типа "Equipment". Реально строка таблицы "equipments" может быть объектом либо подтипа "Platform" либо "Engine". Каждый подтип определяет уникальный getPrice() метод.

 

CREATE TYPE Equipment AS OBJECT (

  name VARCHAR2(24),

  NOT INSTANTIABLE MEMBER FUNCTION getPrice() RETURN NUMBER

) NOT INSTANTIABLE NOT FINAL;

CREATE TYPE Platform UNDER Equipment (

  size NUMBER,

  OVERRIDING MEMBER FUNCTION getPrice() RETURN NUMBER

);

CREATE TYPE BODY Platform AS

  MEMBER FUNCTION getPrice() IS

  BEGIN

    RETURN size * 6

  END getPrice;

END;

CREATE TYPE Engine UNDER Equipment (

  power NUMBER,

  OVERRIDING MEMBER FUNCTION getPrice() RETURN NUMBER

);

CREATE TYPE BODY Engine AS

  MEMBER FUNCTION getPrice() IS

  BEGIN

    RETURN 40 + power * 5

  END getPrice;

END;

 

CREATE TABLE equipments OF Equipment;
INSERT INTO equipments
  VALUES (Platform('Tower X04', 4));
INSERT INTO equipments
  VALUES (Engine('Ford U14', 14));
SELECT name, getPrice() price

  FROM equipments;

 

Результат:

 

name        price

-------------------

Tower X04    24

Ford U14    110

 

Java/Zigzag представление

 

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

 

public abstract class Equipment implements java.io.Serializable {

  public String name;

  public abstract int getPrice();

}//Equipment

 

public class Platform extends Equipment {

  public int size;

  public Platform(String name, int size) {

    this.name = name;

    this.size = size;

  }//Platform

  public int getPrice() {

    return size * 6;

  }//getPrice

}//Platform

 

public class Engine extends Equipment {

  public int power;

  public Engine(String name, int power) {

    this.name = name;

    this.power = power;

  }//Engine

  public int getPrice() {

    return 40 + power * 5;

  }//getPrice

}//Engine

 

Затем следующий Zigzag фрагмент создает программные объекты типа "Platform" и "Engine", отображает их содержимое в базе данных через $mapState, и печатает таблицу с атрибутами "name" и "price" как предыдущий SQL пример.

 

equipment:[

  @po Platform.(Tower X04, 4),

  @po Engine.(Ford U14, 14)

];

$mapState(equipment:);

$printTable(equipment:, name, price);

 

Дополнительно Zigzag обладает уникальной возможностью задавать не только программные объекты, но их атрибуты и методы, как множество. То есть метод или атрибут может быть выражен нечетко, через выражение-запрос:

[object  expression].[method expression]()вызов метода программного объекта,

[ object  expression].[attribute  expression] – обращение к атрибуту программного объекта.

Заключение

Три основных свидетельства, 1.комплексные данные, 2.наследование типа, и 3.объектное поведение, достаточны, чтобы классифицировать РСУБД или СУБД как ОРСУБД или ОСУБД соответственно. Внутренний язык СУБД как SQL или Zigzag - не критерий, а только материал для классификации на логическом уровне. Язык Zigzag соотносится с объектным SQL3, по крайней мере, в объектно-ориентированном представлении данных. Zigzag - более выразителен и помогает работать со структурно более гибкими данными. Однако SQL3 с его схематизацией типа позволяет устанавливать более строгий контроль однородных данных. Основное отличие проявляется в том, что Zigzag может видеть тип как объект данных и объект данных как тип для других объектов. Это позволяет создать иерархию данных, не только иерархию типа, семантически более точно, например:

 

                               продукция(оборудование)

                               /                    \

печатная(периодичность,оборудование:типография)  видео(оборудование:видеокамера,...)

                      /                    \                                     | \

журнал(периодичность:регулярно,...)  книга(назначение,периодичность:нерегулярно)  

/ |                                   /                  |                   \

     беллетристика(назначение:развлечение)  учебник(назначение:образование)  ...

 

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

 

                           продукция(ид,оборудование)

                             /                 \

печатная(периодичность)            видео(...)

                /       \                       /     \

      журнал(...)     книга(назначение)     

 

Объекты "книга", сохраненные в таблице "книги", будут представлены с соответствующим порядком атрибутов:

 

книги = [

  книга('беллетристика', 'типография', 'нерегулярно', 'развлечение'),

  книга('учебник', 'типография', 'нерегулярно', 'образование')

]

 

Как может быть замечено, значения атрибутов не наследуются, так что "типография" и "нерегулярно" повторены в каждом объекте. В будущем SQL должен владеть менее выразительными, чем Zigzag, но разумно простыми конструкциями, чтобы рассматривать типы в роли объектов. Гибкость, основанная на рассмотрении объектов в качестве типов, останется прерогативой таких языков, как Zigzag. Чтобы создать объекты типа  "учебник" и "беллетристика", SQL3 разработчик создаст новые типы и новые таблицы или, что лучше, преобразует существующую таблицу в новые. Zigzag разработчик использует уже существующие объекты "учебник" и "беллетристика" как типы новых объектов. Например, чтобы добавить объект "физика" он может ограничить себя составлением только одной инструкции подобной :учебник:физика(...).