Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

2008 г.

Базы данных. Вводный курс

Сергей Кузнецов

Назад Содержание Вперёд

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

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

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

Напомним, что индивидуальным типом называется 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.194) Далее, поскольку индивидуальный тип является частным типом структурного типа, для индивидуального типа можно определять методы.

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

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

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

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

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

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

23.2.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, атрибуты и методы которого будут наследоваться определяемым структурным типом. Структурные типы, определяемые без использования наследования, называются максимальными супертипами (поскольку у любого из таких типов супертип отсутствует)195). В определениях максимального структурного супертипа или индивидуального типа должен присутствовать раздел представления (AS):

representation ::= predefined_type
    | (attribute_definition_ commalist)

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

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

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

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

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

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

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

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

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

instantiable_clause ::= INSTANTIABLE
    | NOT INSTANTIABLE

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

Обязательный раздел 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, MUMPS200), Pascal, PL/1). Язык Java поддерживается в стандарте в несколько иной манере, чем другие языки. Список параметров метода может быть определен в стиле, более соответствующем стилю SQL-подпрограмм (каждый параметр может принимать неопределенное значение, и не требуется параметр кода возврата). Для этого в качестве характеристики метода нужно указать PARAMETER STYLE SQL. Можно определить список параметров в стиле, более близком стилю различных языков программирования (к параметру, который может принимать неопределенное значение, должен быть добавлен дополнительный параметр-индикатор, и должен быть явно определен выходной параметр кода ответа). В этом случае метод должен иметь характеристику PARAMETER STYLE GENERAL. Наконец, для методов, тела которых будут написаны на языке Java, нужно указать характеристику PARAMETER STYLE JAVA.201)

Любой метод может быть детерминированным (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 (или если характеристика явно не задана), то метод всегда явно выполняется (т. е. происходит вызов соответствующей функции) при вызове с любым набором аргументов.


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

195   Как уже отмечалось ранее, раздел подтипизации может присутствовать только при определении структурного UDT.

196   А в стандарте SQL:2003 и MULTISET.

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

198   Мы вынуждены следовать терминологии стандарта SQL, которая иногда бывает довольно нечеткой. В частности, по отношению к структурным типам используются термины значение (value) во вполне стандартном смысле; местоположение (site) как расширенное понятие переменной (нечто, содержащее значение структурного типа); экземпляр (instance). Последний термин в объектной терминологии обычно используется в том же смысле, что объект класса. В случае SQL это строка типизированной таблицы (см. следующий раздел).

199   Мы снова используем обороты, принятые в стандарте SQL. Заметим, что, хотя смысл неинстанциируемого типа должен быть интуитивно понятен, приведенное определение является очень нечетким. Классическое (не вполне строгое) понятие типа данных основывается на паре <множество_значений, набор_операций>. Поэтому нельзя создать значение типа, можно только выбрать его из соответствующего множества значений. Поэтому, строго говоря, в типе данных не может присутствовать «метод-конструктор», а может иметься (или не иметься) операция выборки значения. У неинстациируемых типов такая операция отсутствует.

200   Теперь этот язык называется M. Вокруг этого языка и его реализаций имеется, в частности, целое семейство СУБД, основанных на так называемой M-технологии. Судя по всему, наиболее успешной представительницей этого семейства является СУБД Cache известной компании InterSystems.

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

Назад Содержание Вперёд

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...