Три манифеста баз данных ретроспектива и перспективы

       

Типы кортежей и отношений


Кортежные типы и типы отношений являются генерируемыми типами, получаемыми путем применения генераторов типа TUPLE и RELATION соответственно. Вот пример использования кортежного типа:

VAR ADDR TUPLE { STREET CHAR,
                CITY   CHAR,
                STATE  CHAR,
                ZIP    CHAR } ;

Здесь определяется кортежная переменная ADDR типа

TUPLE { STREET CHAR, CITY CHAR, STATE CHAR, ZIP CHAR }

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

Умышленно не поддерживается отдельная операция “определения типа кортежа”. Одним из оснований для такого решения является то, что иначе потребовалось бы вводить имя кортежного типа (помимо уже существующего имени TUPLE {…}). Такие дополнительные имена усложнили бы другие аспекты предлагаемого Д&Д подхода, например, решения вопроса о том, когда два типа совпадают.119

По этим соображениям предписывается, чтобы кортежные типыиспользовались “в виде подстановки”, как часть операции, которая определяет кортежную переменную отдельно.

Для кортежных типов требуется поддержка “операций, аналогичных операциям RENAME , project , EXTEND и JOIN из реляционной алгебры”. Вот несколько примеров, не требующих специальных пояснений (в последнем примере присутствует вызов селектора кортежей

– фактически, кортежный литерал):

ADDR RENAME ZIP AS POSTCODE   /* переименование атрибута кортежа */

ADDR RENAME PREFIX ' ST ' AS ' G ' /* то же самое, префиксный вариант */

ADDR   { STATE , ZIP }                        /* проекция кортежа */

EXTEND ADDR ADD NAME ('Clark Kent') AS NAME /* расширение кортежа */



ADDR JOIN                            /* соединение кортежей */
    TUPPLE { NAME NAME ('Clark Kent') ,  COUNTRY 'USA' }


Вот пример присваивания кортежей:

ADDR  :=  TUPLE {STREET 'One Jacob Way' ,
                 CITY   'Reading' ,
                 STATE  'Massachusetts' ,
                 ZIP    '01870' } ;

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

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

VAR STATE_VAR CHAR ;

STATE_VAR := STATE FROM ADDR ;

Явно допускается, чтобы кортежи (и отношения) включали атрибуты, значениями которых были бы кортежи. Операции, которые применяются к этим кортежным значениям, это в точности те же операции языка D , которые применяются к кортежным значениям вообще. Вотпример:

VAR NADDR1 TUPLE { NAME NAME,
                   ADDR TUPLE { STREET CHAR,
                                CITY   CHAR,
                                STATE  CHAR,
                                ZIP    CHAR } } ;

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

NADDR2 { NAME, ZIP }

Это конкретное выражение вычисляет кортеж, который порождается из текущего значения NADDR2 путем “проецирования на все, кроме” атрибутовSTREET , CITY и STATE . И кортежный тип этого порожденного кортежа есть в точности

TUPLE { NAME NAME, ZIP CHAR }

Рассмотрим следующие кортежные типы:

TUPLE { NAME NAME, ADDR TUPLE { STREET CHAR, CITY CHAR,
                                STATE   CHAR, ZIP    CHAR } }

TUPLE { NAME NAME, STREET CHAR, CITY CHAR,


                   STATE   CHAR, ZIP    CHAR }

Будем называть эти два типа TT 1 и TT 2 соответственно. Пусть теперь NADDR 1 и NADDR 2 являются переменными кортежа типов TT 1 и TT 2 , соответственно. Тогда:

  • Выражение

    NADDR2 WRAP { STREET, CITY, STATE, ZIP } AS ADDR

    принимает текущее значение NADDR 2 и “обволакивает” компоненты STREET , CITY , STATE и ZIP этого значения, производя единственный компонент ADDR с кортежным значением. Результат выражения принадлежит, таким образом, типу TT 1 , и тогда (например) допустимо такое присвоение:

    NADDR1  :=  NADDR2 WRAP { STREET, CITY, STATE, ZIP } AS ADDR ;


  • Выражение

    NADDR1 UNWRAP ADDR

    принимает текущее значение NADDR1 и “разворачивает” компонент ADDR (с кортежным значением) этого значения, производя четыре скалярных компонента STREET , CITY , STATE и ZIP . Результат выражения принадлежит, таким образом, типу TT 2 , и тогда допустимо (например) такое присваивание:

    NADDR2  :=  NADDR1 UNWRAP ADDR ;

    Все, что говорилось выше по поводу кортежных типов, с небольшими коррективами применимо к типам отношений, и мы не будем повторяться. Остановимся только на двух дополнительных операциях – GROUP и UNGROUP . Рассмотрим следующие типы отношения:

    RELATION { S# S#, PQ RELATION { P# P#, QTY QTY } … }

    RELATION { S# S#, P# P#, QTY QTY }

    Назовем эти два типа отношения RT 1 и RT 2 соответственно. Пусть теперь SPQ 1 и SPQ 2 – это relvar типов RT 1 и RT 2 соответственно. Тогда:

  • Выражение s

    SPQ2 GROUP { P#, QTY } AS PQ

    (которое можно было бы прочитать как “сгруппировать SPQ 2 по S # ”, гдеS # –единственный атрибут SPQ 2 , не упомянутый в спецификации GROUP ) вырабатывает отношение, определяемое следующим образом. Во-первых, заголовок выглядит следующим образом:

    { S # S #, PQ RELATION { P # P #, QTY QTY } }

    Во-вторых, тело включает ровно по одному кортежу для каждого уникального значения S # в SPQ 2 (и не содержит никаких других кортежей). Каждый кортеж этого тела состоит из соответствующего значения S # (скажем, s ) и значения PQ (скажем, pq ), получаемого следующим образом:



    Во-первых, каждый кортеж SPQ 2 заменяется на кортеж (скажем, x ), в котором компоненты P # и QTY обернуты в компонент (скажем, y ) с кортежными значениями.

    Компоненты y всех таких кортежей x , у которых значение S # равно s , “группируются” в отношение pq , и тем самым генерируется результирующий кортеж со значением S #, равным s , и значением PQ , равным pq

    Таким образом, общий результат относится к типу RT1, и поэтому (например) допустимо следующее присваивание:

    SPQ1  :=  SPQ2 GROUP { P#, QTY } AS PQ ;

    Выражение

    SPQ 1 UNGROUP PQ

    производит отношение, определяемое следующим образом. Во-первых, заголовок выглядит следующим образом:

    { S# S#, P# P#, QTY QTY }

    Во-вторых, тело содержит в точности по одному кортежу для каждой комбинации кортежа в SPQ 1 и кортежа в значенииPQ внутри этого кортежа SPQ 1 (и не содержит никаких других кортежей). Каждый кортеж в этом теле состоит из соответствующего значения S # (скажем, s ) и значений P # и QTY (скажем, p и q ), получаемых следующим образом:

    Во-первых, каждый кортеж SPQ 1 заменяется на множество кортежей, по одному такому кортежу (скажем, x ) для каждого кортежа в значении PQ кортежа SPQ 1 . Каждый такой кортеж x содержит компонент S # , равный компоненту S # из соответствующего кортежа SPQ 1 , и компонент с кортежным значением (скажем, y ), равный некоторому кортежу из компонента PQ того же кортежа SPQ 1 .

    Компоненты y каждого кортежа x , в котором значение S # равняется s , разворачиваются в отдельные компоненты P # и QTY (скажем, p и q ), и тем самым генерируется результирующий кортеж со значением S # , равным s , значением P # , равным p , и значением QTY , равным q .

    Таким образом, окончательный результат относится к типу RT 2, и поэтому допустимо следующее присваивание:

    SPQ2  :=  SPQ1 UNGROUP PQ ;


    Содержание раздела