9. Лекция: Объектные расширения

В последней лекции этого курса мы кратко изложим суть объектных расширений, которые включены в стандарт SQL:1999. Лекция основана не на официальном тексте стандарта (он очень формален и скучен), а на книге Джима Мелтона "Advanced SQL:1999. Understanding Object-Relational and Other Advanced Features" (Morgan Kaufmann Publishers, 2003), которая, по сути, является неформальным описанием семантики (rationale) соответствующей части языка. В указанной книге объектным расширениям языка SQL посвящено более 200 страниц. Естественно, наше изложение будет гораздо более кратким.

Введение

Как известно, язык SQL появился в середине 1970-х гг. при выполнении экспериментального проекта реляционной СУБД System R. Проект выполнялся в компании IBM, и это вполне естественно, потому что именно сотрудник IBM Эдгар Кодд предложил миру идею реляционных баз данных. От System R исходит большинство традиционных средств стандарта SQL:1999 (и SQL:2003), которые мы обсуждали в восьми предыдущих лекциях. Однако в этой лекции речь пойдет о возможностях современных вариантов SQL, которые не имеют отношения к System R (за исключением некоторых экспериментов по представлению сложных объектов средствами SQL) и, вообще говоря, к реляционной модели данных, а именно — о так называемых объектно-реляционных расширениях языка. Чтобы у читателей не возникло впечатление, что объектные расширения появились в языке SQL внезапно, благодаря какому-то озарению разработчиков языка, мы начнем эту лекцию с небольшого (и крайне субъективного) очерка истории объектно-реляционного подхода к организации систем баз данных.

Истоки и краткая история объектно-реляционных баз данных

Пальму первенства в области объектно-реляционных систем управления базами данных (ОРСУБД) оспаривают два весьма известных специалиста в области технологии баз данных – Майкл Стоунбрейкер (Michael Stonebraker) и Вон Ким (Won Kim).

Первые ОРСУБД

Майкл Стоунбрейкер начал работать в области баз данных в начале 1970-х гг. прошлого века в университете Беркли. Его первым всемирно известным проектом была реляционная СУБД Ingres, которая существует и используется до сих пор в двух ипостасях – как свободно распространяемая система (университетская Ingres; код поддерживается в Беркли) и как коммерческая СУБД, принадлежащая компании Computer Associates. В исходном варианте СУБД Ingres отсутствовала поддержка языка SQL (поддерживался собственный язык запросов QUEL), но система уже обладала некоторыми уникальными чертами, которые, с небольшой натяжкой, можно было бы назвать объектными (например, в СУБД Ingres допускалось определение пользовательских процедур, выполняемых на стороне сервера). Кроме того, в проекте Ingres очень большое внимание уделялось управлению правилами.

В 1980-е гг. Майкл Стоунбрейкер возглавлял проект Postgres (вариант этой системы под названием PostgreSQL в настоящее время является весьма популярным свободно доступным продуктом). В Postgres были реализованы многие интересные средства: поддерживалась темпоральная модель хранения и доступа к данным, и в связи с этим был полностью пересмотрен механизм журнализации изменений, откатов транзакций и восстановления БД после сбоев; обеспечивался мощный механизм ограничений целостности; поддерживались ненормализованные отношения (работа в этом направлении началась еще в среде Ingres), хотя и довольно странным способом: в поле отношения мог храниться динамически выполняемый запрос к БД.

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

В начале 1990-х гг. Стоунбрейкер создал компанию Illustra, основной целью которой был выпуск коммерческого варианта СУБД Postgres, получившего название Illustra. В этой системе поддерживались основные идеи Postgres, но уже присутствовала и поддержка языка SQL. В конце 1995 г. компания Illustra была поглощена компанией Informix, и это привело к выпуску в 1996 г. СУБД Informix Universal Server (см. ниже).

Имя Вона Кима стало широко известно во второй половине 1970-х гг., когда он примкнул к участию в экспериментальном проекте компании IBM System R. Наиболее известная ранняя работа доктора Кима была посвящена преобразованию SQL-запросов с целью превращения запросов с вложенными подзапросами в запросы с соединениями.

В 1980-е гг. Вон Ким работал в компании MCC, где успешно выполнил реализацию серии прототипов ООСУБД Orion. В этих прототипах были опробованы многие идеи объектно-ориентированных СУБД. Одной из интересных особенностей проекта было то, что в качестве основного языка программирования использовался объектный вариант известного функционального языка Lisp.

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

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

Возможная конфигурация системы UniSQL
Рис. 19.1.  Возможная конфигурация системы UniSQL

Как показывает рис. 19.1, сервер UniSQL позволяет представлениям работать через "глобальную" схему базы данных S, полученную из двух "фрагментарных" схем баз данных, которые управляются непосредственно UniSQL и СУБД Oracle1).

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

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

В созданной компанией системе поддерживалось расширение стандарта SQL – SQL/X, одновременно включающее и объектно-ориентированные, и реляционные возможности. В одном языке поддерживались возможности и определения данных, и манипулирования ими. В качестве языковых средств программирования приложений поддерживались языки C++ и Smalltalk.

Внедрение объектных расширений в основные РСУБД

В конце 1989 г. группа известных специалистов в области языков программирования баз данных опубликовала документ под названием Манифест систем объектно-ориентированных баз данных (для краткости будем называть его Первым манифестом). Основным поводом к написанию и публикации этого материала было то, что к тому времени существовал ряд систем управления базами данных, которые, по большому счету, объединяла только общая привязанность к объектно-ориентированным языкам программирования. В Первом манифесте была предпринята попытка дать определение системам объектно-ориентированных баз данных. Авторы стремились привести описание основных свойств и характеристик, которыми должна обладать система, претендующая на то, чтобы называться системой объектно-ориентированных баз данных. Первый манифест был написан академическими исследователями; почти все они являлись и являются профессорами различных университетов. Конечно, это нашло свое отражение в стиле Первого манифеста – очень мягком и умеренно рекомендательном (хотя по своему духу предложения этого манифеста были весьма радикальными).

Через год после публикации Первого манифеста вышел в свет Манифест систем баз данных третьего поколения, инициатором которого, очевидно, был Майкл Стоунбрейкер (хотя у документа формально имелось много авторов). Мы говорим об этом с уверенностью, поскольку в этом манифесте повсюду видны идеи Стоунбрейкера, использованные им в проектах Ingres и Postgres.

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

Второй манифест (или, вернее, работы, приведшие к его появлению) имел важные последствия. В 1995 г. компания Informix (ныне входящая в состав IBM) купила компанию Майкла Стоунбрейкера Illustra2), и Стоунбрейкер стал техническим директором Informix. В начале 1996 г. компания Informix объявила о выпуске принципиально нового продукта Informix Universal Server, в котором, как утверждалось, лучшие черты Informix Online Server сочетались с развитыми объектными чертами, присущими Illustra.

К выпуску Informix Universal Server очень ревниво отнеслась компания Oracle, которая немедленно заявила, что у нее готов собственный объектно-реляционный продукт, по всем параметрам превосходящий систему компании Informix. Эта система, получившая название Oracle8, была выпущена в конце лета 1996 г.

Годом позже к группе производителей объектно-реляционных СУБД (ОРСУБД) примкнула компания IBM, выпустившая продукт DB2 Universal Database. Как выяснилось позже, все наиболее важные свойства этого продукта были реализованы еще в 1995 г. в СУБД DB2 for Common Servers. Просто компания IBM предпочла до поры не афишировать свои расширения.

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

Объектные расширения языка SQL были зафиксированы в стандарте SQL:1999. В той или иной мере эти расширения поддерживаются во всех трех перечисленных выше продуктах. В настоящее время ближе всех к стандарту находятся СУБД компании Oracle и DB2 компании IBM.

Объектная модель SQL

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

Объектная модель SQL:1999 включает два основных отличительных компонента – структурные, определяемые пользователями типы данных (User Defined Type – UDT) и типизированные таблицы (Typed Table). Первый компонент позволяет определять новые типы данных, которые могут быть гораздо более сложными, чем встроенные типы данных языка SQL. При определении структурного UDT требуется специфицировать не только содержащиеся в нем элементы данных, но и семантику типа данных, т. е. его поведение на основе интерфейса вызовов методов. Второй компонент – типизированные таблицы – позволяет определять таблицы, строки которых являются экземплярами (или значениями) UDT, с которым явно ассоциируется таблица. Во многих отношениях строка типизированной таблицы похожа на объект класса в объектно-ориентированной системе.

В стандарте SQL:1999 определены два пакета объектных свойств – минимальный (PKG006) и полный (PKG007), которым должны удовлетворять SQL-ориентированные ОРСУБД, претендующие на соответствие стандарту. Ниже будут перечислены свойства, включенные в каждый из пакетов, но смысл этих свойств будет понятен только после прочтения остальных разделов.

Пакет PKG006 включает всего пять свойств:

  • свойство S023 ("Basic structured types") – возможность определять UDT и их методы с ограниченными возможностями;
  • свойство S041 ("Basic reference types") – возможность определять и использовать ссылки на экземпляры UDT, входящие в типизированные таблицы;
  • свойство S051 ("Create table of type") – возможность создания типизированных таблиц;
  • свойство S151 ("Type predicate") – возможность определения точного типа (в иерархии типов) экземпляра UDT;
  • свойство Т041 ("Basic LOB data type support") – возможность определения LOB-типов в смысле SQL (с необязательной поддержкой операций, кроме операций сохранения и полной выборки).

Пакет PKG007 содержит девять дополнительных свойств:

  • свойство S024 ("Enhanced structured types") добавляет к свойству S023 ряд развитых возможностей, в число которых входят возможности кодирования методов на языках, отличных от SQL, сравнения экземпляров UDT и передача экземпляров UDT в качестве параметров различных процедур;
  • свойство S043 ("Enhanced reference types") расширяет свойство S041 возможностями определения ссылок с областью действия, автоматической проверки законности ссылок и т. д.;
  • свойство S071 ("SQL-paths in function and type name resolution") позволяет использовать путевые выражения SQL (SQL-path) в алгоритме разрешения типа;
  • свойство S081 ("Subtables") расширяет возможности свойства S051, допуская организацию иерархии таблиц, аналогичной иерархии типов соответствующих UDT;
  • свойство S111 ("ONLY in query expressions") обеспечивает возможность выборки только экземпляров указанного типа, без экземпляров любого из его подтипов;
  • свойство S161 ("Subtype treatment") позволяет информировать среду SQL о том, что некоторый экземпляр UDT в действительности является экземпляром указанного подтипа;
  • свойство S211 ("User-defined cast functions") разрешает определять подпрограммы, преобразующие экземпляры UDT к другим типам;
  • свойство S231 ("Structured type locators") способствует доступу к экземплярам UDT из прикладных программ;
  • свойство S023 ("Transform functions") позволяет определять подпрограммы, преобразующие значения UDT в значения предопределенных типов данных, и наоборот.

Цели лекции

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

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

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

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

Введя этот необходимый контекст, перейдем к описанию соответствующих механизмов SQL:1999.

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

Один из основных упреков по адресу языка SQL, звучавший, в частности, в Первом манифесте, заключался в отсутствии каких бы то ни было возможностей хранить в базе данных данные, тип которых являлся бы не предопределенным, а определяемым пользователями. Отрицательные последствия отсутствия такой возможности признавались и во Втором манифесте. В SQL:1999 этот дефект был устранен. Как отмечалось в лекции 11, в стандарте поддерживается возможность определения пользователями двух разновидностей UDT – структурных типов (structured type) и индивидуальных типов (distinct types).

Индивидуальные типы

Напомним, что индивидуальным типом называется UDT, основанный на единственном встроенном типе (например, INTEGER). Значения такого типа нельзя прямо использовать в операциях соответствующего базового типа, однако допускается явное приведение значений индивидуального типа к базовому типу. Поясним это на примерах.

Пусть заданы следующие определения индивидуальных типов:

CREATE TYPE EMP_NO AS INTEGER FINAL;
CREATE TYPE DEPT_NO AS INTEGER FINAL;
CREATE TYPE PRO_NO AS INTEGER FINAL;

Таблицу EMP можно определить следующим образом (упрощенный вариант):

CREATE TABLE EMP (
EMP_ID EMP_NO,
EMP_NAME VARCHAR(20),
DEPT_ID DEPT_NO,
PRO_ID PRO_NO);

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

SELECT EMP_NAME
FROM EMP
WHERE EMP_ID > DEPT_ID;

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

SELECT EMP_NAME
FROM EMP
WHERE CAST (EMP_ID TO INTEGER) > CAST (DEPT_ID TO INTEGER);

Аналогичным образом будет отвергнут запрос

SELECT EMP_NAME, EMP_ID + 5
FROM EMP
WHERE DEPT_ID > 630;

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

SELECT EMP_NAME, CAST (EMP_ID TO INTEGER) + 5
FROM EMP
WHERE CAST (DEPT_ID TO INTEGER) > 630;

У читателей могут возникнуть два законных вопроса:

  • почему, вопреки обыкновению, мы не привели формальные синтаксические правила операции определения индивидуального типа?
  • что означает ключевое слово FINAL в приведенных примерах определения индивидуальных типов?

На оба эти вопроса достаточно дать один (возможно, неожиданный) ответ. С формальной точки зрения индивидуальный тип данных является частным случаем структурного типа данных. Обе разновидности UDT определяются единым синтаксисом, который мы обсудим в следующих подразделах. В частности, ключевое слово FINAL играет важную роль в определении структурного типа, указывая на тот факт, что этот тип может использоваться только для создания объектов, а не для порождения новых типов на основе механизма наследования. При определении индивидуальных типов механизм наследования не используется, и поэтому в определении любого индивидуального типа должно присутствовать ключевое слово FINAL.5) Далее, поскольку индивидуальный тип является частным типом структурного типа, для индивидуального типа можно определять методы.

В своих книгах главный редактор стандартов SQL Джим Мелтон постоянно подчеркивает семантическое сходство понятий индивидуального типа данных и домена в смысле SQL ( лекция 11). Более того, утверждается, что в следующих версиях стандарта SQL использование доменов будет сначала объявлено нежелательным, а потом и вовсе будет запрещено. Но я полагаю, что сделать это совсем непросто.

Напомним, что в случае использования SQL-домена:

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

Естественно, эти возможности могут использоваться текущими пользователями стандарта SQL. В то же время в случае использования индивидуального типа данных:

  1. в определении индивидуального типа указывается только базовый тип данных; если столбец определяется на индивидуальном типе данных, то для него обязательно придется специфицировать собственное ограничение целостности;
  2. для значений столбца, определенного на индивидуальном типе данных, не допускаются операции соответствующего базового типа (если не использовать операцию явного приведения типов).

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

Определение структурных типов

Общий синтаксис оператора определения UDT (индивидуального или структурного) определяется следующими правилами:

UDT_definition ::= CREATE TYPE UDT_name
[ subtype_clause ]
[ AS representation ]
[ instantiable_clause ]
finality
[ reference_type_specification ]
[ ref_cast_option ]
[ cast_option ]
[ method_specification_commalist ]

Имя определяемого пользователем типа данных имеет, в общем случае, традиционную для SQL трехзвенную структуру – имя_каталога.имя_схемы.имя_типа. Раздел подтипизации задается в следующем синтаксисе:

subtype_clause ::= UNDER UDT_name

Если этот раздел присутствует в определении UDT, то в нем указывается имя ранее определенного UDT, атрибуты и методы которого будут наследоваться определяемым структурным типом. Структурные типы, определяемые без использования наследования, называются максимальными супертипами (поскольку у любого из таких типов супертип отсутствует)6). В определениях максимального структурного супертипа или индивидуального типа должен присутствовать раздел представления (AS):

representation ::= predefined_type
    | (attribute_definition_ commalist)

Если в разделе представления указывается имя предопределенного встроенного типа, то определяется индивидуальный тип. Указание списка определений атрибутов соответствует определению структурного типа. Заметим, что раздел представления может отсутствовать. В этом случае должен присутствовать раздел подтипизации, и представление заново определяемого структурного типа полностью наследуется из определения структурного UDT, имя которого указано после ключевого слова UNDER.

Определение атрибута структурного UDT

Определение атрибута имеет следующий синтаксис:

attribute_definition ::= attribute_name data_type
    [ reference_scope_check ]
    [ default_clause ]
    [ collate_clause ]

Имя определяемого атрибута должно отличаться от имен всех других атрибутов определяемого типа, включая имена атрибутов, наследуемых от супертипа, и имена атрибутов типа данных определяемого атрибута. Тип данных может быть любым допустимым в SQL типом данных (включая конструируемые типы ARRAY7) и ROW, а также UDT), кроме самого определяемого структурного типа и его супертипов8).

Для атрибута можно объявить значение по умолчанию. Если типом данных атрибута является встроенный тип данных, то значение атрибута объявляется в том же синтаксисе, что и значение столбца по умолчанию в определении таблицы (см. лекцию 12). Если типом данных атрибута является UDT (индивидуальный или структурный), тип ROW или ссылочный тип (см. следующий пункт), то единственным допустимым значением по умолчанию является неопределенное значение (NULL). Если же типом данных атрибута является тип ARRAY, то значением по умолчанию может быть NULL или пустое значение-массив (указывается как ARRAY[]).

Для каждого определения атрибута, в котором типом атрибута является структурный тип, система автоматически генерирует пару методов, имена которых совпадают с именем атрибута. Первый метод является наблюдателем (observer). Он вызывается без явных параметров и выдает значение указанного атрибута в значении того структурного типа, к которому применяется. Второй метод является мутатором (mutator). Он вызывается с одним явным параметром – значением типа атрибута, применяется к некоторому местоположению (столбцу, переменной или параметру), где находится значение определяемого структурного типа, и этот вызов приводит к тому, что значение заменяется новым значением того же типа с измененным соответствующим образом значением данного атрибута.

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

Можно определить инстанциируемый (instantiable) или неинстанциируемый (not instantiable) структурный тип:

instantiable_clause ::= INSTANTIABLE
    | NOT INSTANTIABLE

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

Обязательный раздел finality указывает на возможность или невозможность определения подтипов определяемого структурного типа:

finality ::= FINAL | NOT FINAL

При определении индивидуального типа всегда требуется указывать FINAL. При определении структурного типа в SQL:1999 необходимо указать NOT FINAL. Это требование не обосновано, и в следующих версиях стандарта SQL будет разрешено определять структурные типы, от которых невозможно наследование.

Раздел спецификации ссылочного типа

Хотя типизированные таблицы обсуждаются в следующем разделе, мы вынуждены немного забежать вперед, чтобы ввести синтаксис и пояснить смысл раздела reference_type_specification определения структурного типа. Строки типизированных таблиц обладают всеми характеристиками объектов в объектно-ориентированных системах, включая уникальные идентификаторы, которые могут использоваться для ссылок из других компонентов среды. В SQL:1999 поддерживаются три различных механизма присваивания уникальных идентификаторов экземплярам структурных типов, ассоциированных с такими таблицами (для всех строк таблицы, ассоциированной с данным структурным типом, используется один и тот же механизм). Уникальные идентификаторы экземпляров структурного типа могут представлять собой следующее:

  • значения, генерируемые системой автоматически (system_generated_representation);
  • значения некоторого встроенного типа SQL, которые должны генерироваться приложением при сохранении экземпляра структурного типа как строки типизированной таблицы (user_generated_representation);
  • значения, порождаемые из одного или нескольких атрибутов структурного типа (derived_representation).

Соответственно, синтаксис раздела reference_type_specification определяется следующими правилами:

reference_type_specification ::= system_generated_representation
    | user_defined_representation
    | derived_representation
system_generated_representation :== REF IS SYSTEM GENERATED
user_defined_representation :== REF USING predefined_type
derived_representation ::= REF USING (commalist_of_attributes)

Раздел reference_type_specification может присутствовать только в определении максимального структурного супертипа, т. е. соответствующая спецификация наследуется всеми подтипами этого супертипа. При отсутствии в определении супертипа явного раздела reference_type_specification по умолчанию предполагается наличие раздела REF IS SYSTEM GENERATED.

Разделы спецификации функций явного преобразования типов

Если в определении структурного типа присутствует раздел reference_type_specification и он имеет вид user_generated_representation, то в определении структурного типа должен присутствовать и раздел ref_cast_option (тем самым, раздел ref_cast_option может присутствовать только в определении максимального структурного супертипа). Спецификации этого раздела используются для преобразования предоставленных приложением значений встроенного типа в значения типа REFERENCE (REF) , необходимые для реального выполнения ссылок на строки типизированной таблицы, и обратного преобразования. Синтаксис раздела определяется следующими правилами (подробнее см. в следующем разделе):

ref_cast_option ::= cast_to_ref
    | cast_to_type
cast_to_ref ::= CAST (SOURCE AS REF) WITH identifier
cast_to_type ::= CAST (REF AS SOURCE) WITH identifier

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

cast_option ::= cast_to_distinct
    | cast_to_source
cast_to_distinct ::= CAST (SOURCE_TO_DISTINCT) WITH identifier
cast_to_source ::= CAST (DISTINCT_TO_SOURCE) WITH identifier
Раздел объявления сигнатур методов

В разделе method_specification_commalist объявляются сигнатуры методов, ассоциируемых с определяемым структурным типом. Раздел определяется следующими синтаксическими правилами:

method_specification ::= original_method_specification
    | overriding_method_specification
    | static_field_method_specification
original_method_specification ::= partial_method_specification
    [ SELF AS RESULT ]
    [ SELF AS LOCATOR ]
    [ method_characteristic_list ]
overriding_method_specification ::=
    OVERRIDING partial_method_specification
partial_method_specification :==
    [ INSTANCE | STATIC | CONSTRUCTOR ] 
   METHOD method_name
    SQL_parameter_declaration_list
    return_clause
    [ SPECIFIC specific_method_name ]
method_characteristic ::= language_clause
    | parameter_style_clause
    | deterministic_clause
    | SQL_data_access_indication
    | null_call_clause
specific_method_name ::= [ schema_name . ]
qualified_identifier
static_field_method_specification ::=
    STATIC METHOD method_name ( )
    RETURNS data_type
    [ SPECIFIC specific_method_name ]
    external variable name character_string_literal

Как показывает синтаксис, имеются возможности определять первичные методы (original_method_specification), неприменимые к любому супертипу определяемого структурного типа. Если определяемый тип является подтипом некоторого другого типа, то можно также определить подменяющие методы (overriding_method_specification). Подменяющий метод имеет то же имя и тот же список аргументов, что и метод, определенный в некотором супертипе определяемого типа.

Исходный метод может быть определен как метод экземпляра (INSTANCE), статический метод (STATIC) или метод-конструктор (CONSTRUCTOR). Методы экземпляра действуют над экземплярами определяемого типа. Статические методы не используют экземпляры типа и не влияют на них; такие методы действуют над самим типом. Наконец, методы-конструкторы используются для инициализации экземпляров типа. Поскольку у неинстанциируемого типа не может быть экземпляров, для него могут быть определены только статические методы. Если при определении первичного метода его разновидность не указывается, этот метод считается методом экземпляра.

В сигнатуре метода указывается имя, по которому этот метод будет вызываться (вызывное имя – invocable name). Кроме того, можно указать точное имя метода (specific name), которое может использоваться для уникальной идентификации метода, если его вызывное имя перегружено. Если у метода имеются какие-либо параметры, отличные от неявного параметра SELF, то в определении должен присутствовать заключенный в скобки список пар <имя_параметра, тип_параметра>, разделяемых запятыми. Поскольку методы являются функциями, требуется указать тип возвращаемого значения. Методы могут возвращать значения любого допустимого в SQL типа, даже структурного типа, ассоциированного с методом.

Наконец, у каждого метода имеется набор характеристик метода (method_characteristic). Методы могут быть написаны на языке SQL (более точно, на SQL/PSM) или на любом из языков программирования, поддержка которых предусмотрена в стандарте SQL (Ada, C/C++, COBOL, Fortran, MUMPS11), Pascal, PL/1). Язык Java поддерживается в стандарте в несколько иной манере, чем другие языки. Список параметров метода может быть определен в стиле, более соответствующем стилю SQL-подпрограмм (каждый параметр может принимать неопределенное значение, и не требуется параметр кода возврата). Для этого в качестве характеристики метода нужно указать PARAMETER STYLE SQL. Можно определить список параметров в стиле, более близком стилю различных языков программирования (к параметру, который может принимать неопределенное значение, должен быть добавлен дополнительный параметр-индикатор, и должен быть явно определен выходной параметр кода ответа). В этом случае метод должен иметь характеристику PARAMETER STYLE GENERAL. Наконец, для методов, тела которых будут написаны на языке Java, нужно указать характеристику PARAMETER STYLE JAVA.12)

Любой метод может быть детерминированным (DETERMINISTIC) или недетерминированным (NOT DETERMINISTIC). Детерминированный метод всегда возвращает один и тот же результат, если вызывается с одним и тем же набором аргументов при одном и том же состоянии базы данных. По умолчанию методы считаются недетерминированными.

У каждого метода имеется характеристика, указывающая связь этого метода с SQL. Можно указать следующие варианты:

  • метод не содержит операторов SQL (NO SQL);
  • метод содержит операторы SQL, но не обращается к базе данных (CONTAINS SQL);
  • метод может производить выборку из базы данных, но не обновляет базу данных (READS SQL DATA);
  • в методе допускаются обновления базы данных (MODIFIES SQL DATA).

По умолчанию принимается характеристика CONTAINS SQL. Наконец, для каждого метода можно определить его реакцию на аргументы, являющиеся неопределенными значениями. Если указывается RETURN NULL ON NULL INPUT, то метод всегда возвращает неопределенное значение, если значение любого из его аргументов является неопределенным (независимо от того, что написано в теле функции, реализующей метод). Если же указывается CALLED ON NULL INPUT (или если характеристика явно не задана), то метод всегда явно выполняется (т. е. происходит вызов соответствующей функции) при вызове с любым набором аргументов.

Типизированные таблицы

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

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

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

typed_table_defintion ::= CREATE TABLE typed_table_name
    OF UDT_name
    [ UNDER typed_table_name ]
    [ (typed_table_element_list) ]

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

Подтаблицы и супертаблицы

Далее, при определении типизированной таблицы можно объявить ее подтаблицей некоторой другой типизированной таблицы (имя супертаблицы указывается в разделе UNDER). Таблица R' является собственной подтаблицей супертаблицы R, если R' не совпадает с R (в этом случае таблица R является собственной супертаблицей подтаблицы R'). Супертаблица должна быть ассоциирована со структурным типом, являющимся непосредственным супертипом13) определяемой подтаблицы. Каждый столбец указанной супертаблицы наследуется подтаблицей; наследуются и характеристики столбцов супертаблицы – значения по умолчанию, ограничения целостности и т. д. Эти столбцы называются унаследованными столбцами подтаблицы, и они соответствуют атрибутам UDT подтаблицы, унаследованным от UDT супертаблицы. Кроме того, подтаблица будет содержать по одному столбцу для каждого собственного атрибута ассоциированного структурного типа. Такие столбцы подтаблицы называются заново определенными.

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

Определение элементов типизированной таблицы

Заключительным компонентом определения типизированной таблицы является конструкция typed_table_element_list, являющаяся обобщением конструкции table_element_list, которая используется в определении обычной базовой таблицы (см. лекцию 12). Элемент списка элементов типизированной таблицы определяется следующим синтаксическим правилом:

type_table_element ::= table_constraint_definition
    | self-referencing_column_specification
    | column_options

Как видно из этого правила, в определении типизированной таблицы разрешается указывать табличные ограничения целостности. Если определяемая таблица является подтаблицей некоторой супертаблицы, то в ней не допускается определение ограничения первичного ключа (PRIMARY KEY). Однако если определяется максимальная супертаблица, то в ее определении допускается спецификация PRIMARY KEY (с указанием одного или нескольких столбцов) или спецификация ограничения UNIQUE (с указанием одного или нескольких столбцов) в комбинации с указанием NOT NULL. В определении типизированной таблицы могут также содержаться спецификации ссылочных ограничений целостности. Ссылки могут вести как на типизированную, так и на обычную таблицу.

"Самоссылающийся" (self-referencing) столбец специфицируется в следующем синтаксисе:

REF IS column_name { SYSTEM GENERATED 
    | USER GENERATED | DERIVED }

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

Последней разновидностью элемента типизированной таблицы являются опции столбцов (column_options). Опции столбца можно указывать только для заново определенных столбцов — для унаследованных столбцов это не допускается. Соответствующая конструкция имеет следующий синтаксис:

column_name WITH OPTIONS ::= scope_clause
|default_clause
|column_constraint_definition_list
|collate_clause

Раздел scope_clause может входить в опции только заново определяемого столбца с типом REF (подробности в следующем подразделе). Для заново определяемого столбца некоторого типа символьных строк можно указать раздел collate_clause, чтобы задать желаемый порядок на соответствующем наборе символов. Если требуется указать значение столбца по умолчанию, отличное от значения по умолчанию соответствующего атрибута, ассоциированного с определяемой таблицей структурного типа, можно воспользоваться опцией default_clause. Наконец, для заново определяемого столбца можно указать одно или несколько ограничений, включая проверочные ограничения (см. лекцию 12).

Ссылочные значения и REF-типы

Понятия ссылочных значений и ссылочных (REF) типов являются, по существу, неразделимыми. В SQL:1999 ссылочный тип может использоваться в качестве типа данных столбцов обычных таблиц, атрибутов структурных типов, SQL-переменных и параметров – словом, везде, где можно использовать другие типы данных SQL. Значения местоположения ссылочного типа всегда являются ссылочными значениями строк типизированных таблиц (т. е. значениями самоссылающихся столбцов этих строк).

Для удобства повторим синтаксис спецификации ссылочного типа:

reference_type_specification ::= system_generated_representation
    | user_defined_representation
    | derived_representation
system_generated_representation :== REF IS SYSTEM GENERATED
user_defined_representation :== REF USING predefined_type
derived_representation ::= REF USING (commalist_of_attributes)
Механизмы генерации ссылочных значений

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

  • значениями некоторого встроенного типа SQL (user_defined_representation), которые должны генерироваться приложением каждый раз при сохранении экземпляра структурного типа как строки типизированной таблицы;
  • значениями, порождаемыми из одного или нескольких атрибутов структурного типа;
  • значениями, автоматически генерируемыми системой.

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

Таблица 19.1. Спецификации ссылочного типа и самоссылающегося столбца
reference_type_specificationself-referencing_column
REF USING predefined_type USER GENERATED
REF FROM commalist_of_attributes DERIVED
REF IS SYSTEM GENERATED SYSTEM GENERATED

Если для некоторого структурного типа выбран вариант пользовательской генерации ссылочных значений, то ответственность за поддержание уникальности таких значений лежит на пользователе. Конечно, ограничения PRIMARY KEY или UNIQUE, определенные на уровне максимальной супертаблицы семейства типизированных таблиц, могут гарантировать отсутствие в любой таблице этого семейства дублирующих ссылочных значений, но в SQL:1999 отсутствуют какие-либо средства, предотвращающие повторное использование ссылочных значений из удаленных строк в самоссылающихся столбцах новых строк.

Преобразование задаваемых пользователем ссылочных значений к ссылочному типу

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

ref_cast_option ::= cast_to_ref
    | cast_to_type
cast_to_ref ::= CAST ( SOURCE AS REF ) WITH identifier
cast_to_type ::= CAST ( REF AS SOURCE ) WITH identifier

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

Для решения именно этой проблемы и предназначена конструкция ref_сast_option. В этой конструкции вводятся имена двух SQL-функций, первая из которых служит для преобразования ссылочных значений, обеспечиваемых приложением, к соответствующему REF-типу при вставке или обновлении строк типизированной таблицы (SOURCE AS REF). Вторая функция преобразует значения REF-типа к соответствующему встроенному типу данных при выборке строк из типизированной таблицы (REF AS SOURCE). Система автоматически генерирует обе подпрограммы, и конструкция ref_сast_option позволяет лишь назначить подпрограммам имена. Если конструкция ref_сast_option явно не включается в определение структурного типа с REF USING predefined_type, то имена подпрограммам назначаются системой. Единственным преимуществом явного назначения имен является возможность явного вызова этих функций при написании SQL-операторов, содержащих выражения REF-типа, которые нужно привести к соответствующему встроенному типу. Заметим, что такие функции невозможно написать вручную, поскольку правила отображения зависят от реализации SQL.

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

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

Спецификация ссылочного типа при объявлении столбцов и атрибутов

Самоссылающиеся столбцы всегда имеют REF-тип. Конкретный REF-тип зависит от двух факторов:

  • структурного типа, ассоциированного с типизированной таблицей: REF-тип всегда связан с некоторым структурным типом;
  • выбранного способа генерации ссылочных значений; эта информация задается в определении структурного типа и не присутствует в спецификации ссылочного типа.

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

reference_type ::= REF (referenced_type) [ SCOPE table_name ]
referenced_type ::= UDT_name

UDT_name должно задавать имя типа (referenced_type), на экземпляры которого будут указывать значения ссылочного типа. REF-тип может использоваться в качестве типа атрибута структурного типа, и в этом случае referenced_type может быть тем же самым, что и определяемый структурный тип. Во все остальных случаях referenced_type должен являться некоторым существующим структурным типом.

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

Поддержка согласованности ссылок

Никакое ссылочное значение никогда не идентифицирует какую-либо строку, кроме той, с которой оно было ассоциировано с самого начала. Если эта строка удаляется, то значение ничего не идентифицирует и никогда не может быть связано с другой строкой. Из этого следует, что система должна каким-либо образом узнавать о том, идентифицирует ли данное ссылочное значение какую-то хранимую строку или ничего не идентифицирует (является висящей ссылкой). Но как система может это узнать, не потратив множество ресурсов? Отчасти здесь может помочь раздел SCOPE. В этом разделе указывается одна таблица, в которой строки должны существовать для всех значений данного местоположения, типом данных которого является некоторый REF-тип. (В будущих версиях стандарта SQL, по всей видимости, будет разрешено указывать в разделе SCOPE список имен типизированных таблиц или даже использовать некоторую конструкцию, означающую "все таблицы, ассоциированные с данным структурным типом".)

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

reference_scope_check ::= REFERENCES ARE [ NOT ] CHECKED
    [ ON DELETE referential_action ]

Если указывается REFERENCES ARE NOT CHECKED или если раздел SCOPE не задается, то в определяемом местоположении можно хранить любое ссылочное значение, независимо от того, является ли оно значением самоссылающегося столбца какой-либо таблицы, на строку которой предположительно указывает ссылка. В этом случае система не гарантирует, что ссылочное значение действительно указывает на строку (но, конечно, это значение должно быть значением правильного типа – REF-типа указанного структурного типа).

Если же указывается REFERENCES ARE CHECKED, то каждый раз при сохранении значения в определяемом столбце, поле или атрибуте система обращается к указанной в разделе SCOPE таблице, чтобы убедиться в том, что в ней имеется строка, значение самоссылающегося столбца которой совпадает с сохраняемым ссылочным значением. Кроме того, если указывается REFERENCES ARE CHECKED, то можно также указать ссылочное действие, которое должно выполняться при удалении строки, идентифицируемой ссылочным значением. Как обычно (см. лекцию 12), возможными ссылочными действиями являются RESTRICT, CASCADE, SET NULL и NO ACTION. Если ссылочное действие явно не указывается, по умолчанию принимается NO ACTION. (Для поля строчного типа (ROW TYPE) и атрибута структурного типа допускается только NO ACTION.)

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

Выборка данных из типизированных таблиц

Приведем несколько примеров операций выборки данных из типизированных таблиц, а также кратко обсудим операции обновления таких таблиц. Для этого сначала определим структурные типы EMP_T, PROGRAMMER_T и DEPT_T, а также соответствующие типизированные таблицы (упрощенный вариант).

CREATE TYPE EMP_T AS (
EMP_NAME VARCHAR(20),
EMP_BDATE DATE,
EMP_SAL SALARY,
DEPT REF (DEPT))
INSTANTIABLE
NOT FINAL
REF IS SYSTEM GENERATED
INSTANCE METHOD age ()
RETURNS DECIMAL (3,1);
CREATE TYPE PROGRAMMER_T UNDER EMP_T AS (
PROG_LANG VARCHAR (10))
INSTANTIABLE
NOT FINAL;
CREATE TYPE DEPT_T AS (
DEPT_NO INTEGER,
DEPT_NAME VARCHAR(200),
DEPT_MNG REF (EMP))
INSTANTIABLE
REF IS SYSTEM GENERATED
NOT FINAL;
CREATE TABLE EMP OF EMP_T
(REF IS DEPT_ID SYSTEM GENERATED,
DEPT WITH OPTIONS SCOPE DEPT);
CREATE TABLE PROGRAMMER OF PROGRAMMER_T UNDER EMP;
CREATE TABLE DEPT OF DEPT_T
(REF IS EMP_ID SYSTEM GENERATED,
DEPT_MNG WITH OPTIONS SCOPE EMP);

Следует отметить, что с типизированными таблицами можно работать, как с обычными таблицами14). Поэтому, в частности, возможен следующий запрос.

SELECT EMP_NAME
FROM EMP
WHERE EMP_SAL < 20000.00;
Пример 19.1. Найти имена всех служащих, размер заработной платы которых меньше 20000.00.

В соответствии с семантикой SQL:1999, при выполнении запроса из примера 19.1 сначала будет произведена выборка имен служащих, удовлетворяющих условию, из таблицы EMP, затем – из таблицы PROGRAMMER , и эти промежуточные результаты будут скомбинированы в окончательный результат путем применения операции объединения (UNION). Но предположим, что нас интересуют только те служащие, получающие зарплату, не превышающую 20000.00, которые не являются программистами (пример 19.2). Тогда можно применить формулировку запроса, в которой присутствует спецификация ONLY:

SELECT EMP_NAME
FROM ONLY (EMP)
WHERE EMP_SAL < 20000.00;
Пример 19.2. Найти имена всех служащих, которые не являются программистами, размер заработной платы которых меньше 20000.00.

Естественно, в запросах к типизированным таблицам можно использовать ссылки.

SELECT EMP_NAME, DEPT -> DEPT_NAME
FROM EMP
WHERE EMP_SAL < 20000.00;
Пример 19.3. Найти имена и названия отделов, где работают служащие, размер заработной платы которых меньше 20000.00.

В SQL:1999 операция ".->" называется операцией разыменования (dereferencing), но в обиходе ее можно считать операцией перехода по ссылке (в нашем примере DEPT ссылается на DEPT_NAME). Можно неформально трактовать ссылочные значения как указатели на строки типизированных таблиц.

Может показаться неожиданным, что запрос из примера 19.3 выбирает значения из таблицы DEPT, хотя в разделе FROM этого запроса она даже не упоминается. Дело в том, что выполнение операции разыменования фактически приводит к выполнению соединения таблиц EMP и DEPT, делая в запросе столбец DEPT_NAME "видимым".

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

SELECT EMP_NAME, DEPT -> DEPT_MNG -> EMP_NAME
FROM EMP
WHERE EMP_SAL < 20000.00;
Пример 19.4. Найти имена служащих и имена руководителей их отделов для служащих, получающих зарплату, не превышающую 20000.00.

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

SELECT DEPT_MNG -> EMP_NAME, DEPT_MNG -> age ()
FROM DEPT
WHERE DEPT_NO = 605;
Пример 19.5. Найти имя и возраст руководителя отдела 605.

Наконец, имеется возможность полностью выбрать экземпляр структурного типа, идентифицируемый ссылочным значением (в SQL:1999 это называется разрешением ссылки – reference resolution).

SELECT DEREF (DEPT_MNG)
FROM DEPT
WHERE DEPT_NO = 605;
Пример 19.6. Получить полные данные о руководителе отдела 605.

В этом случае результатом запроса будет являться таблица, включающая один столбец структурного типа EMP_T. Единственным значением этого столбца будет экземпляр (значение) этого структурного типа, соответствующий служащему-руководителю отдела 605.

Операции обновления типизированных таблиц выполняются очевидным образом. Операция INSERT вставляет указанные строки в указанную таблицу. Операции DELETE и UPDATE удаляют или модифицируют строки в иерархии таблиц, корнем которой является указанная таблица, если в операции не содержится ONLY. Если же специфицировано ONLY, то удаляются или модифицируются только строки указанной таблицы.

Типизированные представления

Наряду с типизированными базовыми таблицами в SQL:1999 поддерживаются типизированные представления, иначе называемые представлениями, на которые можно ссылаться (referenceable views). Иногда такие представления также называют объектными представлениями, поскольку данные, видимые через представление, соответствуют строкам типизированных таблиц, поведение которых во многом похоже на поведение объектов в объектно-ориентированных системах. Между типизированными базовыми таблицами и типизированными представлениями имеется большое сходство, но есть и несколько отличий, связанных с различиями базовых таблиц и представлений.

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

Определение типизированного представления задается в следующей синтаксической форме:

view_definition ::= CREATE VIEW table_name
    OF UDT_name
    UNDER table_name
    (view_element_commalist)
    AS query_expression
    [ WITH [ levels_clause ] CHECK OPTION ]
view_element ::= self_referencing_column_specification
    | column_name WITH OPTIONS scope_clause

Указываемое UDT_name должно быть именем существующего структурного типа. Как и в определении обычных представлений, в разделе AS указывается выражение запроса. В случае типизированных представлений это выражение запроса должно основываться на единственной типизированной таблице (базовой таблице или представлении). Эта типизированная таблица должна быть ассоциирована с тем же структурным типом, что и определяемое представление. Такую таблицу иногда называют базисной таблицей представления.

Типизированное представление можно определить как подпредставление другого типизированного представления. В этом случае структурный тип, ассоциированный с определяемым представлением, должен являться непосредственным подтипом структурного типа, который ассоциирован с суперпредставлением, специфицируемым в разделе UNDER. Базисная таблица определяемого представления должна являться собственной подтаблицей или собственным подпредставлением – не обязательно непосредственным – базисной таблицы непосредственного суперпредставления определяемого представления.

В определение типизированного представления может входить один или несколько элементов column_name WITH OPTIONS scope_clause. Если представление определяется как подпредставление другого типизированного представления, то в его определении не должна содержаться спецификация самоссылающегося столбца. Если определяется максимальное суперпредставление (т. е. в определении не содержится раздел UNDER), то эта спецификация может присутствовать. Если спецификация присутствует, то она может содержать только конструкции USER GENERATED или DERIVED (из этого следует, что нельзя определить типизированное представление, в ассоциированном структурном типе которого присутствует спецификация REF IS SYSTEM GENERATED). При указании USER GENERATED степень определяемого представления на единицу больше числа атрибутов ассоциируемого структурного типа; дополнительным столбцом является самоссылающийся столбец. В случае указания DERIVED дополнительный столбец не появляется, поскольку значение самоссылающегося столбца порождается из тех же столбцов, из которых порождается значение самоссылающегося столбца базисной таблицы.

Заключение

Если обратиться к истории, выяснится, что попытки расширения функциональности СУБД, изначально основанных на реляционном подходе, предпринимались уже на ранних стадиях разработки таких систем. Классическими примерами являются проекты System R компании IBM, где разработчики пытались обеспечить возможности работы со сложными объектами путем расширения SQL, и Ingres (университет Беркли), где Майкл Стоунбрейкер предлагал механизм определения пользовательских типов данных на основе представлений и хранимых процедур. Однако новый толчок к расширению SQL-ориентированных СУБД объектными свойствами был получен со стороны объектного мира после публикации Первого манифеста.

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

Развитие объектно-реляционного подхода нашло отражение в языке SQL. Гигантский стандарт SQL:1999 позволяет хотя бы сопоставлять отдельные реализации, хотя ни одна компания полностью его не поддерживает. Как можно заметить, разработчики стандарта SQL пошли на существенно большее сближение с объектно-ориентированным подходом к организации систем баз данных, чем это предполагалось во Втором манифесте. В особенности это проявляется в механизмах типизированных таблиц, ссылочных типов и ссылочных значений: типизированные таблицы похожи на экстенты классов, а ссылочные значения – на объектные идентификаторы. Однако во многом это сходство является внешним – за путевыми выражениями в стиле ODMG по-прежнему скрываются операции соединения таблиц.

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

И последнее замечание, на котором мы закончим этот курс. Несмотря на некоторую критику в адрес языка SQL, высказанную в начале лекции 11, мы потратили на обсуждение этого языка половину курса и больше его практически не критиковали. Не означает ли это, что язык все-таки очень хорош или что автор питает к нему особую привязанность? Конечно же нет! В языке SQL имеется множество слабых мест, неточностей и даже прямых ошибок. Если задаться целью продемонстрировать все промахи языка SQL, то этот курс никогда бы не закончился, а его читатели так и не узнали бы, что представляет собой язык в целом.

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

  1)   Вопросы интеграции данных выходят за пределы тематики этого курса. Однако следует сделать два замечания. Во-первых, проблематика обеспечения доступа к разнородным данным через некоторую глобальную, или концептуальную схему интересует сообщество баз данных в течение нескольких десятков лет. Существовали многочисленные попытки обеспечить интеграцию баз данных, представленных во всех возможных моделях (сетевой, иерархической, реляционной, объектно-ориентированной). С точки зрения теории решение проблемы возможно, но на практике это приводит к очень сложным с технической точки зрения реализациям, обладающим крайне низкой производительностью. Во-вторых, в MCC в 1980-е годы был создан весьма успешный прототип системы, интегрирующей SQL-ориентированные базы данных. Должно быть понятно, что такая интеграция существенно проще в техническом смысле, поскольку глобальная и фрагментарные схемы представлены в близких понятиях. Похоже, что проект UniSQL в большой степени базировался и на этой работе.
  2)   Компания Illustra была создана Стоунбрейкером для коммерциализации разработанной под его руководством свободно доступной СУБД Postgres.
  3)   Конечно, это не модель данных в смысле Кодда.
  4)   Далеко не факт, что ориентация на язык Java была правильным решением. По мнению автора данного курса, причиной являются отнюдь не уникальные достоинства языка Java (обсуждение этого языка не является задачей автора), а то, что во время разработки стандарта SQL:1999 язык Java был особенно моден. Помимо прочего, заметим, что для языка Java (насколько известно автору) никогда не определялась формальная объектная модель.
  5)   Кстати, не очень понятно, по каким причинам в стандарте SQL не поддерживается наследование для индивидуальных типов. Конечно, этот механизм существенно более полезен для структурных типов, но его вполне можно было бы реализовать и для индивидуальных типов.
  6)   Как уже отмечалось ранее, раздел подтипизации может присутствовать только при определении структурного UDT.
  7)   А в стандарте SQL:2003 и MULTISET.
  8)   Последнее ограничение является непонятным. Его можно обойти, например, следующим образом. Пусть структурный тип T' определяется как подтип типа T, и мы хотим включить в представление типа T' атрибут a типа T. Тогда предварительно определим тип T'' как подтип типа T в точности с тем же представлением. Тогда ничто не помешает определить в представлении типа T' атрибут a типа T''.
  9)   Мы вынуждены следовать терминологии стандарта SQL, которая иногда бывает довольно нечеткой. В частности, по отношению к структурным типам используются термины значение (value) во вполне стандартном смысле; местоположение (site) как расширенное понятие переменной (нечто, содержащее значение структурного типа); экземпляр (instance). Последний термин в объектной терминологии обычно используется в том же смысле, что объект класса. В случае SQL это строка типизированной таблицы (см. следующий раздел).
  10)   Мы снова используем обороты, принятые в стандарте SQL. Заметим, что, хотя смысл неинстанциируемого типа должен быть интуитивно понятен, приведенное определение является очень нечетким. Классическое (не вполне строгое) понятие типа данных основывается на паре <множество_значений, набор_операций>. Поэтому нельзя создать значение типа, можно только выбрать его из соответствующего множества значений. Поэтому, строго говоря, в типе данных не может присутствовать "метод-конструктор", а может иметься (или не иметься) операция выборки значения. У неинстациируемых типов такая операция отсутствует.
  11)   Теперь этот язык называется M. Вокруг этого языка и его реализаций имеется, в частности, целое семейство СУБД, основанных на так называемой M-технологии. Судя по всему, наиболее успешной представительницей этого семейства является СУБД Cache известной компании InterSystems.
  12)   Этот абзац, в частности, показывает, как много нужно знать технических (и не только технических) подробностей, чтобы реально освоить технику определения UDT в среде SQL.
  13)   Тип T является непосредственным супертипом типа T' в том и только том случае, когда T является супертипом T', и не существует такого типа T'', что T является супертипом T'', и T'' является супертипом T.
  14)   По крайней мере, в той же синтаксической форме.