1999 г
Уважаемые читатели!
В своей февральской заметке Крис Дейт пишет об очень важной статье Эдварда Кодда, в которой он (Кодд) описывает первый реляционный язык, Alpha, основанный на реляционном исчислении кортежей. Мне кажется, что эта работа Кодда имела исключительно важное практическое значение. Она породила язык Майкла Стоунбоейкера QUEL и оказала очень важное влияние на развитие языка SQL. Для меня лично язык Alpha был первым, для которого наша группа под руководством Виктора Петровича Иванникова и при основном вкладе со стороны Александра Юрьевича Бякова начала исследовать возможности реализации. Это было очень поучительно. В первой части своего материала г-н Дейт касается средств манипулирования данными в языке Alpha. В следующем месяце, судя по всему, будут рассматриваться возможности выборки данных.
Всегда ваш, Сергей Кузнецов
Intelligent Enterprise, No 2, February 1999
Data Sublanguage Alpha
C.J. Date
(www.intelligententerprise.com/991602/online2.shtml)
Взгляд на собственное предложение Кодда по поводу реляционного языка
В статье прошлого месяца я упоминал тот факт, что Кодд первоначально сам предложил язык баз данных -- "Подъязык данных Alpha" -- основанный на реляционном исчислении. Он описал этот язык (хотя только неформально) в 1970-м г. [1] и потом более полно в 1971-м г. [2]; в действительности, язык Alpha, вероятно, был первым реляционным языком, описанным кем бы то ни было и где бы то ни было. Хотя сам язык Alpha никогда не был реализован, он оказал очень большое влияние на разработку последующих языков, включая, в частности, QUEL и, в меньшей степени, SQL. Более того, язык Alpha также включал некоторые полезные идеи (такие, как частичные запросы), которые сегодня все еще не поддерживаются в широком масштабе. Мне хочется взглянуть на некоторые выдающиеся свойства Alpha. В качестве основного ресурса я буду использовать [2] (называя ее в дальнейшем "статьей про Alpha" или иногда просто "статьей Кодда"); я буду упоминать [1] только в связи с некоторыми интересными идеями, которые не вошли в [2].
Прежде, чем погрузиться в конкретику Alpha, позвольте мне упомянуть, что статья про Alpha включает также краткий список "принципиальных доводов в пользу реляционной модели".
Рабочие пространства
Поскольку язык Alpha ориентирован именно на то, чтобы быть подъязыком данных, требуется некоторый механизм для обмена данными с основным языком, в который встраивается Alpha. Для этой цели служат рабочие пространства. Основная идея состоит в том, что данные, выбираемые из базы данных, помещаются в указанное рабочее пространство, в котором они могут быть доступны средствами основного языка; аналогичным образом, данные, генерируемые основным языком, могут быть помещены в такое рабочее пространство и затем пересланы в базу данных посредством другой операции Alpha. Тем самым, с точки зрения Alpha рабочее пространство содержит отношение (или иногда кортеж); с точки зрения основного языка оно содержит передставление такого отношения или кортежа в виде массива (этот "массив" содержит специальную строку с именами столбцов).
Опора Alpha на рабочие пространства, по существу, означает, что язык опирается на концепцию двухуровневого хранения, т.е. пользователь ощущает резкое различие между данными в основной памяти и данными в базе данных. Это различие -- которое, конечно, является следствием того, что Alpha - это именно подъязык данных -- сохранилось во многих других языках, включая SQL. Оно также составляет предмет некоторой критики [6]. В The Third Manifesto мы с Хью Дарвеном полагаем, что пользователи должны иметь оперировать в терминах одноуровневого хранения; пересылка данных между памятью разного уровня является деталью реализации и должна быть сокрыта от пользователя.
Обзор Alpha
Кодд явно устанавливает, что "не считает особенно важным синтаксис Alpha"; однако он вынужден использовать некоторый синтаксис при описании языка, и буду использовать здесь тот же синтаксис. Ключевые слова будут выделяться жирным шрифтом, чтобы выделить их в окружающем тексте.
Следующая сводка обеспечивает некоторое представление о возможностях языка:
- Введение и удаление имен доменов: DAMAIN, DROP
- Объявление и разрушение отношений: RELATION, DROP
- Объявление переменных с областью определения (кортежных переменных): RANGE
- Выборка: GET
- Вставка: PUT
- Обновление: HOLD, UPDATE, RELEASE
- Удаление: DELETE
- "Конвейерный режим": OPEN, CLOSE
Рассмотрим более пристально каждую из этих возможностей.
Операции определения данных
Начну с доменов. В Alpha упоминались только "простые" домены -- т.е. представляемые в терминах "простых" типов данных, таких как CHAR(5), NUM(4,0). Другими словами, домены не намного отличаются от того, что называется "DISTINCT types" в SQL3, а не являются полными абстрактными типами данных -- ADT -- во всей их красе. (С другой стороны, в статье ничего не говорится по поводу того, что они не могут быть полными ADT!) Вот пример определения (и разрушения) домена на языке Alpha:
DOMAIN CITY CHAR(15)
DROP CITY
Одним из достоинств статьи является то, что -- в отличие от некоторых предшествовавших статей -- в ней проводится явное различие между доменами и атрибутами. В действительности, используется соглашение, по которому имена атрибутов принимают конкретный вид
[role-name] domain-name
где необязательное имя роли является квалификатором, требуемым только в том случае, когда один домен используется в отношении более одного раза. "Одно из важных преимуществ [этой схемы состоит в том, что она допускает] систематическое управление преобразованиями всех вхождений… Примером… является совместное изменение всех номеров деталей независимо от того, где они встречаются…". Как кажется, здесь Кодд, по крайней мере, частично недооценивает ту роль, которую мог бы иметь каталог в процессе такой трансформации -- странное упущение при том, что позже в статье явно упоминается каталог. (Действительно, одним из многих первооткрытий, содержащихся в статье про Alpha, является суждение о том, что сам каталог следует структурировать тем же самым образом, что и другие данные: а именно, как отношения.) Другими словами, я не уверен, что соглашение об именовании атрибутов является хорошим; на самом деле, в нем не принимается во внимание вопрос об именах атрибутов порождаемых отношений.
Обращаясь теперь к отношениям -- более точно, к тому, что Кодд называет "изменяемыми во времени" отношениями -- заметим, что в большей части статьи применяется подразумеваемое предположение, согласно которому все такие отношения являются базовыми; таким вопросам, как определение представлений и в особенности обновление представлений, практически не уделяется внимание (хотя в более ранней статье [1] содержится краткое обсуждение феномена, связанного с представлениями и называемого "миграцией доменов", что я затрону в своей следующей заметке). В определении такого отношения указываются имя отношения, имена атрибутов (и соответствующих доменов) и первичный ключ. Вот пример такого определения (и соответствующего DROP):
RELATION S (S#, SNAME_NAME, STATUS, CITY) KEY S#
DROP S
Операции манипулирования данными
Выборка: Вот пример выборки на языке Alpha ("Получить имена поставщиков и названия городов для поставщиков, которые поставляют все детали"):
RANGE S SX
RANGE SP SPX
RANGE P PX
GET W1 (SX.SNAME, SX.CITY):
ALL PX SOME SPX (SPX.S# = SX.S# AND SPX.P# = PX.P#)
В этом примере W1 - имя применяемого рабочего пространства; двоеточие можно воспринимать как "где" или "такое, что", а ALL и SOME обозначают кванторы всеобщности и существования соответственно. В действительности, в [2] для ALL и SOME используются стандартные логические символы (как и для связок AND, OR и т.д.); в отличие от этого, в [1] применяются ключевые слова, что я предпочитаю. Согласно [2] кванторы можно переместить в объявления RANGE, например:
RANGE S SX
RANGE SP SPX SOME
RANGE P PX ALL
GET W1 (SX.SNAME, SX.CITY):
SPX.S# = SX.S# AND SPX.P# = PX.P#
Замечание: В [2] никогда не упоминается возможность определения переменной на области значений, являющейся, например, объединением двух базовых отношений, и вообще на чем-то более сложном, чем одно базовое отношение. (Я обсуждал эту возможность в своей заметке в прошлом месяце.) Кроме того, в ранней статье [1] использовалось ключевое слово DUMMY вместо RANGE в тех случаях, когда требуется разновидность "фиктивной" переменной (я также обсуждал это предыдущей заметке).
Операции GET также допускают:
- Ссылки на отношения в рабочих пространствах, как если бы они были в базе данных. В таких ссылках используются имена рабочих пространств как если бы они были именами отношений.
- Спецификация упорядочения результата (аналогично ORDER BY в языке SQL) с использованием ключевых слов UP и DOWN.
- Спецификация квоты для ограничения числа выбираемых кортежей. Однако в статье не рассматривается вопрос о том, что должно произойти, если результирующее отношение определяется неоднозначно [10].
- Использование некоторых истинностных функций (TOP и BOTTON). TOP и BOTTON - это аналоги Alpha описанных в [10] функций IS_NTH_LARGEST и IS_NTH_SMALLEST.
- Использование некоторых агрегатных функций (COUNT, TOTAL, MAX, MIN, AVERAGE в целевом списке (однако термин "агрегатные функции" не используется). Замечание: В исходной версии статьи упущен из вида тот факт, что, по крайней мере, для TOTAL и AVERAGE аргументом является мультимножество, а не множество (дубликаты не должны удаляться до выполнения агрегации). Позже Кодд признал эту ошибку [3].
Немного отходя от основной темы, замечу, что в Alpha прямо не допускается использование агрегатных функций в условии выборки; используется сокращенная версия, называемая функциями образов (которые могут также применяться и в списке выборки). Я опускаю здесь детали, хотя, если говорить честно, не думаю, что функции образов относятся к числу лучших идей Alpha. На самом деле, они могли быть источником странного синтаксиса вызова агрегатных функций и в QUEL, и в SQL и сложности обоих языков, связанной с этим неортодоксальным синтаксисом. (Эти сложности порождаются тем фактом, что аргументы функции частично определяются контекстом вместо того, чтобы -- что было бы более ортодоксально -- полностью определяться тем, что содержится в скобках, непосредственно следующих за именем функции [7,8].)
В Alpha также допускается покортежное выполнение выборки в конвейерном режиме (аналогично FETCH через курсор в языке SQL). Например (в предположении тех же, что и раньше определений RANGE):
OPEN GET W1 (SX.SNAME, SX.CITY):
SPX.S# = SX.S# AND SPX.P# = PX.P# UP SX.CITY
GET W1
/* теперь текущая строка обрабатывается программой на основном языке */
.…
CLOSE W1
Здесь при каждом выполнении GET W1 из результата запроса в OPEN выбирается следующий кортеж. (Обратите внимание на спецификация упорядочения UP в этом OPEN.) Конечно, конвейерный режим открывает возможность дискредитации системы, поскольку его можно использовать для выполнения того, что по существу является "ручной навигацией" (вместо автоматической навигации, обычно являющейся предпочтительной в реляционной системе) [4]. Но конвейерный режим или что-то в этом роде является существенным, если язык, как в случае Alpha, - это подъязык данных.
Вставка: Операция PUT пересылает набор кортежей из указанного рабочего пространства в указанное отношение. Например:
PUT W2 SP
("Вставить кортежи из рабочего пространства W2 в отношение SP".) Можно также указать и порядок, чтобы дать знать системе, что вставляемые кортежи упорядочены некоторым образом в рабочем пространстве. На самом деле, в связи с этим понятием я ощущаю небольшой дискомфорт; мне кажется, что здесь слишком сильно перемешаны идеи логического и физического уровней.
Возможна также "конвейерная вставка":
OPEN PUT W2 S
….
/* конструирование текущей строки программой на основном языке */
PUT W2
….
CLOSE W2
Обновление: Вот пример обновления данных ("Переместить всех парижских поставщиков в Рим"):
HOLD W3 (SX.S#, SX.CITY): SX.CITY = 'Paris'
/* установить значение 'Rome' столбца CITY в каждом кортеже */
/* рабочего пространства с использованием основного языка */
UPDATE W3
В языке Alpha требуется, чтобы обновляемые строки были сначала выбраны с помощью HOLD. (Отсутствует аналог беззаботного "поискового UPDATE" языка SQL.) Целевой список HOLD должен содержать в точности одну переменную с областью значений, что означает, что UPDATE будет применяться только к одному (базовому) отношению. (Как говорит Кодд, "[ограничение одной переменной с областью определения] позволяет избежать неоправданной сложности". И всего-то!) Если пользователь решит после всего не выполнять UPDATE, то может быть выполнена операция RELEASE W3, чтобы освободить сохраняемые кортежи.
Замечание: Хотя в [2] это не говорится, целевой список HOLD должен включать все атрибуты первичного ключа обновляемого отношения, чтобы система могла узнать, какие кортежи вы обновляете [3]. Сами атрибуты первичного ключа не могут напрямую обновляться с помощью последовательности HOLD…UPDATE, но для достижения аналогичного эффекта можно использовать удаления и вставки.
Для HOLD…UPDATE/RELEASE также возможен конвейерный режим (аналогично UPDATE CURRENT в языке SQL). Странно, что нет аналога DELETE CURRENT. Однако это упущение не является важным, поскольку -- как я утверждаю в [9] -- идея обновления или удаления (а также и вставки) одного кортежа в любом случае является плохой. Операции обновления следует всегда выполнять над множествами.
Вероятно, можно сказать, что части Alpha, связанные с модификацией, не относятся к сильным сторонам языка; действительно приведенное обсуждение порождает много вопросов, которые просто не затрагиваются в статье.
Удаление: Операция DELETE в основном проста, если оставить в стороне приведенную ранее критику, применимую к операциям модификации в целом. Вот пример:
DELETE SX: SX.S# = 'S1'
В своей следующей заметке я завершу обсуждение подъязыка данных Alpha.
Литература
- E.F. Codd: "Notes on a Data Sublanguage." IBM internal memo (January 19, 1970).
- E.F. Codd: "A Data Base Sublanguage Founded on the Relational Calculus." IBM Research report RJ893 (July 26, 1971). Republished in Proc. 1971 ACM SIGFIDET Workshop on Data Description, Access and Control, San Diego, Calif. (November 1971).
- E.F. Codd: Private communication (March 14, 1972).
- E.F. Codd and C.J. Date: "Interactive Support for Nonprogrammers: The Relational and Network Approaches." IBM Research Report RJ1400 (June 6, 1974). Republished in Randall J. Rustin (ed.), Proc. ACM SIGMOD Workshop on Data Description, Access and Control, Vol. II, Ann Arbor, Michigan (May 1974). Also in C.J. Date, Relational Database: Selected Writings. Reading, Mass.: Addison-Wesley (1986). See also E.F. Codd: "The Significance of the SQL / Data System Announcement." IBM Report (January 30, 1981).
- Hugh Darwen and C.J. Date: "The Third Manifesto." ACM SIGMOD Record 24, No. 1 (March 1995). Version Two of this document is in preparation in the time of writing.
- C.J. Date: "An Introduction to the Unified Database Language (UDL)." Proc. 6th International Conference on Very Large Data Bases, Montreal, Canada (September/October 1980). Republished (in considerably revised form) in C.J. Date, Relational Database: Selected Writings, Addison-Wesley (1986).
- C.J. Date: "A Critique of the SQL Database Language." ACM SIGMOD Record 14, No. 3 (November 1984). Republished in C.J. Date, Relational Database: Selected Writings. Reading, Mass.: Addison-Wesley (1986).
- C.J. Date: A Guide to Ingres. Reading, Mass.: Addison-Wesley (1987).
- C.J. Date: "It's All Relations!" Database Programming & Design 8, No. 1 (January 1995).
- C.J. Date: "Quota Queries." Database Programming & Design 9, Nos. 7-9 (July-September 1996).
- C.J. Date (with Hugh Darwen): A Guide to the SQL Standard (4th ed.). Reading, Mass.: Addison-Wesley (1997).