Главная » SQL, Базы данных » ОПЕРАТОРЫ и реляционная модель

0

До сих пор все определения операторов, применявшиеся в этой главе, относились либо к селекторам, либо к операторам ТНЕ_, а теперь рассмотрим определения операторов в целом. В качестве первого примера ниже показан определяемый пользователем оператор ABS для встроенного типа RATIONAL.

OPERATOR ABS ( Z RATIONAL ) RETURNS RATIONAL ; RETURN ( CASE

WHEN Z > 0 . 0 THEN +Z WHEN Z < 0.0

THEN -Z END CASE ) ; END OPERATOR

;

Оператор ABS (сокращение от "absolute value" — абсолютное значение)  определен в терминах только одного параметра, z, имеющего тип RATIONAL, И возвращает результат того же типа. Поэтому любой вызов оператора ABS,  например ABS (AMTl + AMT2), является по определению выражением типа RATIONAL.

Рассматриваемый в следующем примере оператор DIST (сокращение от  "distance between" — расстояние между двумя точками) принимает два параметра одного и того же определяемого  пользователем  типа  (POINT)  И   возвращает  результат другого  типа

(LENGTH).

OPERATOR DIST ( P1 POINT, P2 POINT ) RETURNS LENGTH ; RETURN { WITH THE_X ( P1 ) AS   X1 ,

THE_X ( P2) AS X2 ,

THE_Y ( P1) AS Y1 ,

THE_Y ( P2) AS Y2 :

LENGTH ( SQRT (( X1 X2 ) ** 2

+ ( Y1 Y2 ) ** 2 ) ) )

;

END OPERATOR ;

Предполагается, что селектор LENGTH принимает параметр типа RATIONAL. Кроме того, заслуживает внимания то, как применяется конструкция WITH ДЛЯ  присваивания имен результатам некоторых подвыражений. Эта конструкция будет  широко использоваться в следующих главах.

В качестве следующего примера рассматривается один из обязательных  операторов сравнения, "=" (сравнение на равенство7), для типа POINT.

7  Приведенный здесь оператор "сравнения на равенство" было бы точнее назвать оператором  сравнения на идентичность, поскольку выражение vl = v2 принимает истинное значение, если и только если значения vl и v2 по сути являются полностью одинаковыми.

OPERATOR EQ ( P1 POINT, P2 POINT   )    RETURNS BOOLEAN ; RETURN ( THE_X ( P1 ) = THE_X   (    P2 ) AND

THE_Y ( P1 ) = THE_Y {  P2 )

) ;

END OPERATOR ;

Следует отметить, что здесь в выражении, которое входит в состав оператора RETURN, используется встроенный оператор "=" для типа RATIONAL. В целях упрощения с этого момента будем считать, что в качестве оператора проверки на равенство может использоваться обычное инфиксное обозначение "=" (для всех  типов, т.е. не только для типа POINT); здесь не приведены рассуждения на  тему,  касающуюся того, как инфиксные обозначения могут быть определены на практике, поскольку по сути такое решение относится просто к сфере синтаксиса.

Ниже приведен оператор ">" для типа QTY.

OPERATOR GT ( Q1 QTY, Q2 QTY ) RETURNS BOOLEAN ; RETURN ( THE_QTY ( Ql ) > THE_QTY ( Q2 )

) ; END OPERATOR ;

Здесь в выражении в составе оператора RETURN используется встроенный оператор ">" для типа INTEGER. И в данном случае подразумевается, что начиная с этого момента в качестве  данного  оператора  может  использоваться  обычное  инфиксное  обозначение, причем не только для типа QTY, но и для всех так называемых порядковых типов. (По определению порядковым типом называется тип, к которому применим оператор ">". Простым примером непорядкового типа является POINT.)

Наконец, ниже приведен пример определения оператора обновления (все  предыдущие примеры относились к типу операторов, предназначенных только для чтения, в которых не допускается обновлять что-либо, за исключением, возможно, локальных переменных)8. Вполне очевидно, что в этом определении  вместо спецификации RETURNS применяется спецификация UPDATES; операторы обновления не возвращают значение и должны вызываться с помощью явно заданных операторов CALL [3.3].

OPERATOR REFLECT ( Р POINT ) UPDATES P ; BEGIN ;

THE_X ( P ) := THE_X ( P ) ;

THE_Y ( P ) := THE_Y ( P ) ; RETURN ; END ; END OPERATOR ;

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

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

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

ТН Е _ Х                                         (                                         Р                                         )                                           :    =    .    .    .    ;

фактически присваивает значение компоненту X (декартового возможного представления) переменной формального параметра, соответствующей  параметру Р. Безусловно, любой формальный параметр, который необходимо обновить с помощью оператора обновления (в частности, путем присваивания значения псевдопеременной ТНЕ_), должен быть задан именно как переменная, а  не в виде некоторого более общего выражения. Псевдопеременные могут быть вложенными, как показано ниже.

VAR LS LINESEG ;

ТНЕ_Х ( THE_BEGIN ( LS ) ) := 6.5 ;

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

ТНЕ_Х ( Р ) := ТНЕ_Х ( Р ) ;

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

. Р := CARTESIAN ( ТНЕ_Х ( Р ) , THE_Y ( Р ) ) ; Аналогичным образом, следующий оператор присваивания ТНЕ_Х ( THE_BEGIN ( LS ) ) := б.5 ;

является логически эквивалентным приведенному ниже.

LS := LINESEG ( CARTESIAN ( 6.5,

THE_Y ( THE_BEGIN ( LS ) )

) , THE_END ( LS ) ) ;

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

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

Глава 5. Типы    181

отдельных присваиваний [3.3]. Например, можно заменить два оператора присваивания в определении оператора REFLECT следующим оператором множественного присваивания.

ТНЕ_Х    (  Р   )    :=    ТНЕ_Х    (Р)

THE_Y   (   Р   )    :=     THE_Y   ( Р)

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

Примечание. Поскольку операция множественного присваивания  рассматривается как одна операция, "в процессе" подобного присваивания проверка целостности не производится; в действительности этот факт является одной из основных причин, по которым мы стремимся в первую очередь обеспечить поддержку множественного присваивания. Дополнительные сведения по этой теме приведены в главах 9 и 16.

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

DROP  OPERATOR  REFLECT  ;

Данный конкретный оператор должен быть определяемым пользователем, а не встро-.

енным.

Преобразования типов

Еще раз рассмотрим следующее определение типа.

TYPE S# POSSREP { CHAR } ;

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

S#    ( ‘ S I ‘ )

(Этот вызов возвращает определенный номер поставщика.) Поэтому можно сделать вывод, что данный селектор  s# может неформально рассматриваться  как оператор преобразования типа, который преобразует символьные строки в номера поставщиков. Аналогичным образом, селектор Р# может рассматриваться как оператор преобразования, который преобразует символьные строки в номера деталей, а селектор QTY — как оператор преобразования, преобразующий целые числа в значения количества, и т.д.

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

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

TYPE WEIGHT POSSREP { D DECIMAL (5,1)

CONSTRAINT D > 0.0 AND D < 5000.0 } ;

Если w относится к типу WEIGHT, то выражение

THE_D    (   W   )

фактически   преобразует   вес,   обозначенный   переменной  w,   в  десятичное   число

D E C I M A L ( 5, 1) .

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

…   WHERE   Р#   =    ‘Р2′

В данном случае левый операнд относится к типу Р#, а правый — к типу CHAR, поэтому по указанным выше соображениям попытка провести такое сравнение должна окончиться неудачей из-за ошибки, связанной с несоответствием типов (фактически ошибка, связанная с несоответствием типов, должна обнаруживаться на этапе компиляции). Но концептуально здесь происходит  именно то, что система определяет возможность использования "оператора преобразования" Р# (иными словами, селектора Р#) для преобразования операнда операции сравнения типа CHAR в тип Р#, и поэтому фактически преобразует эту операцию сравнения в представленную ниже.

…   WHERE   Р#   =   Р#    (‘Р2′)

После этого операция сравнения становится допустимой.

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

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

необходимости, например, как показано ниже.

CAST_AS_CHAR ( 53 0.00 )

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

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

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

WEIGHT + QTY  /* Суммирование веса детали с количеством деталей в поставке */

WEIGHT * QTY  /* Умножение веса детали на количество деталей в поставке   */

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

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

WEIGHT > QTY EVEN > ODD

(Во втором примере предполагается, что операнд EVEN относится к типу четного  целого числа EVEN_INTEGER, а операнд ODD — к типу нечетного целого числа ODD_INTEGER, причем семантика этих типов является очевидной.) Поэтому и в данном случае первое выражение не имеет смысла, а второе действительно имеет смысл. Поэтому операторы, которые могут быть определены для сочетаний весов и количеств, по-видимому, не должны включать ">", а для четных и нечетных целых чисел применение  оператора ">" является вполне допустимым10. (Что касается проблемы определения того,  какие  операторы являются  допустимыми для  тех  или  иных  типов,  следует отметить,  что  по  традиции  в  большей  части  литературы  по  базам  данных,  включая  первые

несколько изданий настоящей книги, рассматривались только операторы сравнения и игнорировались все прочие операторы, такие как"+" и "*".)

Заключительные замечания

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

■     Первый и наиболее важный вывод состоит в том, что система должна иметь ин формацию о том, какие именно выражения являются допустимыми и каким должен

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

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

10  На практике типы EVEN_INTEGER и ODD_INTEGER вполне могли быть определены как  подтипы типа INTEGER, и в этом случае оператор ">", вероятно, был бы унаследован от этого последнего типа (см. главу 20).

также включать логический тип (или истинностное значение), поскольку в ином случае ни один оператор сравнения не будет допустимым!

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

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

Источник: Дейт К. Дж., Введение в системы баз данных, 8-е издание.: Пер. с англ. — М.: Издательский дом «Вильямс», 2005. — 1328 с.: ил. — Парал. тит. англ.

По теме:

  • Комментарии