Главная » SQL, Базы данных » ПЕРВОЕ СЕРЬЕЗНОЕ ЗАБЛУЖДЕНИЕ ОБЪЕКТНОГО ПОДХОДА

0

Начнем с приведенной ниже цитаты из Третьего Манифеста [3.3].

"[Прежде чем] мы сможем рассмотреть вопрос о [сближении между] объектами и отношениями  более подробно, необходимо проанализировать  чрезвычайно важный предварительный вопрос, который приведен ниже".

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

■     домен = класс объекта;

■     переменная отношения = класс объекта.

Теперь перейдем к строгому обоснованию того, что первое из этих уравнений является правильным, а второе — неправильным.

В действительности то, что первое уравнение является правильным, вполне очевидно, поскольку и классы объектов, и домены представляют собой просто типы. Безусловно, если исходить из того, что переменные отношения — это переменные, а классы — это типы, должно быть также сразу же ясно, что второе уравнение является неправильным (переменные и типы — не одно и то же); именно по этой причине в [3.3] приведено категорическое утверждение, что переменные отношения — это не домены. Тем не менее, многие специалисты, а также разработчики некоторых программных продуктов, фактически руководствуются вторым уравнением, т.е. совершают ошибку, которую мы называем первым серьезным заблуждением (The First Great Blunder). Поэтому было бы очень поучительным внимательное изучение второго уравнения, что и будет сделано в этой главе.

Примечание. Остальная часть данного раздела в основном представляет собой более или менее буквальную выдержку из [3.3].

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

CREATE OBJECT CLASS EMP ( EMP# CHAR(5), ENAME

CHAR(20), SAL NUMERIC, HOBBY CHAR(20), WORKS_FOR CHAR(20) ) … ;

Здесь ЕМР#, ENAME и т.д. — открытые переменные экземпляра. (Автор  специально определил их как относящиеся к простым встроенным типам, а не к определяемым пользователем типам; к тому же аналогичного соглашения автор будет для упрощения придерживаться во всех остальных примерах данной главы.) Теперь рассмотрим следующее определение базовой переменной отношения на языке SQL.

CREATE TABLE EMP

( EMP#     CHAR(5)   NOT     NULL, ENAME    CHAR(20)  NOT     NULL, SAL          NUMERICNOT NULL,

HOBBY    CHAR(20)  NOT    NULL,

WORKS_FOR CHAR(20)  NOT    NULL ) …;

Эти два определения, безусловно, кажутся очень похожими и поэтому идея их приравнивания друг другу выглядит очень соблазнительной. Поэтому (как уже было сказано) в некоторых системах, включая определенные коммерческие  программные продукты, фактически сделано именно это. Таким образом, рассмотрим эту проблему более внимательно.  Точнее,  примем  за  основу  только  что  приведенное  предложение  CREATE TABLE и рассмотрим ряд  дополнений к нему (против чего кое-кто мог бы возразить), предназначенных для того, чтобы оно стало "более похожим на объектное".

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

критические замечания, которые будут приведены ниже в данном разделе, с соответствующими  оговорками,  относятся  к  любой  системе,  которая  основана  на  уравнении "переменная отношения = класс объекта".

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

отношения или, возможно, из той же самой переменной отношения (?!). В данном примере можно заменить первоначальное предложение CREATE TABLE следующей коллекцией предложений (рис. 26.1).

Рис. 26.1. Атрибуты, содержащие кортежи (указатели на кортежи), —

CREATE TABLE EMP

( ЕМР#     CHAR(5) NOT NULL, ENAME     CHAR(20)NOT NULL, SAL                                         NUMERIC NOT NULL,

HOBBY    ACTIVITY    NOT NULL, WORKS_FOR COMPANY NOT NULL ) ;

CREATE TABLE ACTIVITY

( NAME     CHAR(20)    NOT NULL, TEAM     INTEGER NOT NULL ) ;

CREATE TABLE COMPANY

( NAME     CHAR(20)    NOT NULL, LOCATION CITYSTATE   NOT NULL ) ;

CREATE TABLE CITYSTATE

( CITY     CHAR(20)    NOT NULL,

STATE    CHAR(2) NOT NULL ) ;

Пояснение. Атрибут HOBBY (Увлечение) в переменной отношения EMP объявлен  как относящийся к типу ACTIVITY (Вид спорта), а сам тип ACTIVITY, в свою очередь, представляет собой переменную отношения с двумя атрибутами, NAME И TEAM, где TEAM задает количество игроков в команде, соответствующей названию NAME; например, возможным видом спорта может быть тот вид футбола, в  котором команда состоит из 11 игроков, (Soccer, 11). Поэтому каждое значение  HOBBY представляет  собой фактически пару значений — значение NAME И значение TEAM (точнее, это — пара значений, которая в настоящее время присутствует в качестве кортежа в переменной отношeния ACTIVITY).

Примечание. Обратите внимание на то, что мы уже нарушили предписание Третьего Манифеста, согласно которому  переменные отношения  не являются  доменами,  поскольку домен для атрибута HOBBY определен как переменная отношения ACTIVITY. Дополнительные сведения по этой теме приведены ниже в данном разделе.

Аналогичным образом, атрибут WORKS_FOR в переменной отношения ЕМР  объявлен как относящийся к типу COMPANY, a COMPANY также представляет  собой переменную отношения с двумя атрибутами, один из которых определен  как относящийся к типу CITYSTATE, представляющему собой еще одну переменную отношения с двумя атрибутами,  и т.д. Иными  словами,  все  переменные отношения ACTIVITY,  COMPANY  и CITYSTATE рассматриваются  как типы (домены) и вместе с тем как переменные отношения. Такое же  утверждение, безусловно, является справедливым и применительно к самой переменной отношения ЕМР.

Таким образом, это первое дополнение примерно аналогично тому, согласно которому допускается включение одних объектов в другие и тем самым поддерживается понятие иерархии вложения (см. главу 25).

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

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

Замечания, аналогичные приведенным в предыдущем абзаце, относятся  также ко второму дополнению, которое состоит в том, что должны быть разрешены атрибуты со значением в виде отношения;  из этого следует, что в качестве значений атрибутов должно быть разрешено использовать множества кортежей из некоторой другой переменной отношения или, возможно, даже из той же самой переменной отношения (?!). Например, предположим, что служащие могут иметь произвольное количество увлечений, а не только одно (рис. 26.2), как показано в следующем объявлении.

CREATE TABLE EMP

( ЕМР#   CHAR(5)                                             NOT NULL, ENAME   CHAR(20)                                          NOT NULL,

SAL    NUMERIC                                           NOT NULL,

HOBBIES SET OF ( ACTIVITY ) NOT NULL,

WORKS_FOR COMPANY                                         NOT NULL ) ;

Пояснение. Теперь значение HOBBIES в каждом конкретном кортеже переменной отношения  ЕМР  (концептуально)  представляет  собой множество  пар,  т.е кортежей (NAME, TEAM) из переменной отношения ACTIVITY В количестве от нуля и больше. Поэтому данное второе дополнение приблизительно аналогично тому, что разрешается использовать объекты, которые включают объекты коллекций — это более сложная версия иерархии вложения.

Примечание. Кстати, следует отметить, что в том конкретном программном продукте,

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

Рис. 26.2. Атрибуты, содержащие множества кортежей (указателей на кортежи), —

нерекомендуемый вариант

Третье дополнение состоит в том, что в переменных отношения должно быть разрешено предусматривать связанные с ними методы (т.е. операторы), например, следующим образом.

CREATE TABLE EMP

( EMP#   CHAR(5) ,                                           NOT NULL,

ENAME   CHAR (20)                                           NOT NULL, SAL     NUMERIC                                            NOT NULL, HOBBIES SET OF ( ACTIVITY ) NOT NULL,

WORKS_FOR                                                                                     COMPANY     NOT NULL ) METHOD RETIREMENT_BENEFITS ( ) : NUMERIC ;

Пояснение. Здесь RETIREMENT_BENEFITS — это метод, который принимает в качестве фактического  параметра заданный  кортеж ЕМР  и  вырабатывает результат типа

Рис. 26.3. Переменные отношения, которые определены как суперклассы и

NUMERIC. Последнее дополнение в области объявления состоит в том, что должны быть разрешены подклассы, например, следующим образом (рис. 26.3):

подклассы, — нерекомендуемый вариант

CREATE TABLE PERSON

( SS#                                        CHAR(9)                                           NOT NULL, BIRTHDATE DATE                                                                                NOT NULL, ADDRESS  CHAR(50)                                           NOT NULL ) ;

CREATE TABLE EMP AS SUBCLASS OF PERSON

( EMP#     CHAR(5)                                           NOT NULL, ENAME    CHAR (20)                                           NOT NULL, SAL                                        NUMERIC                                            NOT NULL,

HOBBIES  SET OF ( ACTIVITY ) NOT NULL,

WORKS_FOR COMPANY                                           NOT NULL ) METHOD RETIREMENT_BENEFITS ( ) : NUMERIC ;

Пояснение. Теперь переменная отношения ЕМР имеет три дополнительных  атрибута (SS#, BIRTHDATE и ADDRESS), которые унаследованы от  переменной отношения PERSON (поскольку каждый экземпляр EMP "is a", т.е. представляет собой также экземпляр4 PERSON, выражаясь неформально). Если бы таблица PERSON имела какие-либо методы, то они также могли быть унаследованы.

Примечание. Здесь PERSON и ЕМР представляют собой примеры того, что иногда, соответственно, именуется супертаблицами и подтаблицами. Дополнительное обсуждение (и критика) этих понятий приведено в [ 14.13], а также в разделе 26.6.

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

■  Обозначения пути (например, ЕМР. WORKS_FOR. LOCATION. STATE). Следует отметить, что такие выражения, вообще говоря, могут возвращать скаляры, кортежи или отношения. Заслуживает также внимания то, что в последних двух случаях

4 Автор приносит свои извинения за то, что использует здесь такой нечеткий термин, как "экземпляр", но если бы он попытался воспользоваться точной терминологией значений и переменных, то схема, которую он пытается описать (настолько объективно, насколько это возможно), безусловно, потеряет весь сысл.

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

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

(    ‘ Е 0 0 1 ‘ ,     ‘Smith’,     $50000,

( ( ‘Soccer’, 11 ), ( ‘Baseball’, 9 ) ),

( ‘IBM’, ( ‘San Jose’, ‘CA’ ) ) )

■     Реляционные операторы сравнения (например, SUBSET, SUBSETEQ и т.д.).

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

SUBSET фактически подразумевается оператор выделения строгого подмножества,

а под оператором SUBSETEQ — оператор выделения подмножества (!?).

■     Операторы для прохождения по иерархии классов (например, для одновременной выборки информации ЕМР и PERSON).

Примечание. Здесь также необходимо соблюдать осторожность, поскольку  вполне может оказаться, что запрос на выборку информации PERSON наряду с получением соответствующей информации ЕМР приведет к получению результата, не являющегося отношением. Это означает, что нарушено крайне важное реляционное свойство замкнутости, а это вполне может привести с разрушительным последствиям. В этой связи в [26.41], где такой результат называется непредсказуемым возвратом ("jagged return"), просто приведено легкомысленное замечание, что "клиентская программа должна обладать способностью справляться со сложностями непредсказуемого возврата".

■     Способность вызывать методы, например, в конструкциях SELECT и WHERE

(в терминологии SQL).

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

На этом можно завершить краткий обзор того, как уравнение "переменная отношения = класс объекта" реализуется на практике. Так что же здесь неправильно? Прежде всего следует отметить (как было указано выше), что переменная отношения — это прежде всего переменная, а класс — это тип, поэтому в чем они могут быть аналогичными? Даже этого первого замечания должно быть с точки зрения элементарной логики достаточно для того, чтобы понять, что сама идея равенства "переменная отношения = класс объекта" является мертворожденной. Но по данному вопросу можно привести гораздо более полезные сведения, поэтому согласимся еще немного продлить это заблуждение… Ниже  рассматриваются  некоторые  дополнительные  вопросы,  также  заслуживающие внимания.

■     Из уравнения "переменная отношения = класс объекта" следует другие уравне ния: "кортеж = объект" и "атрибут = (открытая) переменная экземпляра". Итак, (как было показано в главе 25), настоящий класс объекта (по крайней мере, ска лярный или инкапсулированный класс объекта) имеет методы и не имеет открытых

переменных  экземпляра, поэтому  класс  объекта  переменной  отношения  имеет  открытые  переменные  экземпляра  и  включает  методы  только  в  качестве  необязательного средства (т.е. определенно не является инкапсулированным). Таким образом, снова нельзя найти ответ на этот вопрос  — как могут быть одинаковыми эти два понятия?

■     Между   двумя   определениями   атрибутов   (например)   "SAL    NUMERIC"   И "WORKS_FOR COMPANY" имеется важное различие. Дело в том, что NUMERIC пред ставляет собой настоящий тип данных (в равной степени его можно рассматривать как настоящий, хотя и примитивный домен); он налагает не зависящее от времени ограничение на значения, которые допускаются использовать в атрибуте SAL. В отличие от него, COMPANY — это не настоящий тип данных; ограничение, которое он налагает на значения, разрешенные для применения в атрибуте WORKS_FOR, за висят от времени (безусловно, они зависят от текущего значения переменной от ношения COMPANY). Фактически, как было указано выше, здесь не учитывается различие между переменной отношения и доменом или, если угодно, различие между коллекцией и классом.

■     Выше было показано, что на первый взгляд разрешено включать в объекты корте жей другие такие объекты; например, может показаться, что объекты ЕМР включа ют объекты COMPANY. Но фактически в данном случае дело обстоит не так, что одни объекты включают другие объекты; вместо этого они содержат указатели на содержащиеся в них объекты, и пользователи должны иметь абсолютно четкое по нимание этого вопроса. Например, предположим, что пользователь каким-то об разом обновляет некоторый конкретный кортеж COMPANY (СМ. рис. 26.1). Тогда это обновление станет сразу же видимым во всех кортежах ЕМР, которые содержат данный кортеж COMPANY.

Примечание. Автор не утверждает, что такой эффект нежелателен, он хочет  лишь подчеркнуть, что об этом необходимо сообщить пользователю. Но сообщить об этом пользователю — значит рассказать ему о том, что "модель", показанная на рис. 26.1, является неправильной, поскольку кортежи ЕМР не  содержат кортежи COMPANY, а содержат вместо этого указатели на кортежи COMPANY (как уже было сказано).

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

а) Можно ли вставить кортеж ЕМР и задать значение для "содержащегося" в  нем кортежа COMPANY, который в настоящее время не существует в  переменной отношения COMPANY? Если ответ является положительным,  тот факт, что атрибут WORKS_FOR определен как принадлежащий к типу COMPANY, мало что означает, поскольку он не позволяет так или иначе в какой-то заметной степени налагать ограничения на операцию INSERT. А если ответ отрицателен, то операция INSERT становится слишком сложной, поскольку пользователь будет вынужден задавать не  только  имя  существующей  компании  (т.е.  значение  внешнего  ключа),  как потребовалось   бы   в   аналогичной   ситуации   с   использованием   реляционных объектов,  но  и  весь  существующий  кортеж  COMPANY.  Более  того, задавая весь существующий кортеж COMPANY, пользователь в лучшем случае

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

б)  Предположим, что нужно ввести для данных о компаниях правило ON DELETE RESTRICT (для того чтобы попытки удалить данные о компании оканчивались неудачей, если в базе данных остается информация о сотрудниках этой компа нии). Вполне можно допустить, что это правило должно быть предписано с по мощью процедурного кода, скажем, с помощью некоторого метода M (обратите внимание на то, что переменная отношения ЕМР не имеет внешнего ключа, за которым можно было бы закрепить декларативную версию этого правила). Кроме того, обычные операции DELETE языка SQL теперь не должны выпол няться над переменной отношения COMPANY, кроме как с помощью кода, ко торый реализует данный метод М. Каким образом можно было бы предписать выполнение этого требования? Аналогичные замечания и вопросы, безуслов но, относятся и к другим правилам работы с внешними ключами, таким как ON DELETE CASCADE.

в)  Следует также отметить, что удаление кортежа ЕМР может не повлечь за собой выполнение каскадной операции удаления соответствующего кортежа COMPANY, несмотря на видимое впечатление, что кортеж ЕМР содержит такой кортеж COMPANY.

Из всего сказанного выше следует, что, строго говоря, речь больше не идет о реляционной модели. Фундаментальный объект данных больше не  является отношением, содержащим значения; его, скорее, можно назвать "отношением" в кавычках (фактически вообще не настоящим отношением в том смысле, который подразумевается реляционной моделью), содержащим значения и указатели. Иными словами,    здесь    подорвана    концептуальная    целостность    реляционной    модели. Примечание. Термин концептуальная целостность (conceptual integrity) был введен Фредом  Бруксом  (Fred Brooks), который дал ему следующее определение [26.3]: "[Концептуальная] целостность — это наиболее важное требование в проектировании системы. Лучше всего исключить из системы некоторые аномальные средства, чтобы воплотить в ней единый набор проектных требований, чем создавать систему, которая реализует много важных, но  независимых и нескоординированных требований" (курсив оригинала). А в другой своей работе, которая вышла через 20 лет, он добавляет: "Качественный, изящный программный продукт должен представлять  собой  реализацию…  целостной  теоретической  модели…  Для  удобства эксплуатации… наиболее важным фактором является… [концептуальная]  целостность… Теперь я в этом убежден еще больше, чем когда-либо. Концептуальная целостность является основой обеспечения качества  продукта" (полужирный шрифт и курсив оригинала).

■      Предположим, что определено представление V как проекция  переменной отношения ЕМР только (скажем) по атрибуту HOBBIES. Разумеется, что V — это также переменная отношения, но  производная,  а не базовая. Поэтому, если уравнение "переменная отношения = класс объекта" является правильным, то V — также класс. Но какой это класс? К тому же классы имеют методы; какие методы применяются к представлению V?

Очевидно, что "класс" ЕМР имеет только один метод, RETIREMENT_BENEFITS, и этот метод, безусловно, не применим к "классу" V. Фактически, вряд ли можно назвать разумным такое предположение,  что  какие-либо методы, применимые к "классу" ЕМР, будут относиться и к "классу" V; а то же самое, безусловно, касается других   представлений.    Поэтому   (в   целом)   создается   впечатление,   что   к результатам  проекции  не  могут  применяться  вообще  какие-либо  методы;  это означает, что результат проекции, каким бы он ни был, в действительности вообще не является классом. (Мы могли бы, разумеется, просто "назвать" его классом, но от этого он не стал бы таковым! Дело в том, что это был  бы класс, имеющий открытые переменные экземпляра и не имеющий методов, притом что уже было отмечено,  что  настоящий  инкапсулированный  класс  имеет  методы  и  не  имеет открытых   переменных   экземпляра.)   Вполне   очевидно,   что   фактически   те специалисты, которые ставят знак равенства между переменными  отношения и классами, имеют в виду именно базовые переменные отношения, но забывают о производных переменных отношения. (Безусловно, указатели, описанные выше, представляют  собой  указатели   на  кортежи  в  базовых,  а  не  в  производных переменных    отношения.)    Но    проведение    различия    между    базовыми    и производными  переменными отношения указанным образом является ошибкой высшего    порядка,   поскольку   задача   определения   того,   какая   переменная отношения является базовой, а какая производной (и это очень важно), решается полностью    произвольно,    в    зависимости    от    потребностей     конкретного приложения (как было сказано в описании принципа взаимозаменяемости в главе 10). ■ Наконец, какие домены  поддерживаются?  По-видимому, те, кто считает правильным уравнение "переменная отношения = класс объекта", стараются в основном избегать обсуждения вопроса о доменах, скорее всего, по той причине, что не  могут   определить,  каким  образом  домены  как  таковые  вписываются  в разработанную ими общую схему. Тем не менее, как известно, понятие доменов является одним из существенных.

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

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

Любопытно поразмыслить над тем, с чем связано возникновение первого серьезного заблуждения. По мнению автора, его корни лежат в том, что в сообществе специалистов в области объектно-ориентированных систем до сих пор нет согласия по поводу толкования некоторых терминов (как было отмечено в главе 25). В частности, сам термин объект не имеет общепризнанного и согласованного толкования; именно поэтому автор данной книги предпочитает его не использовать. •■ Несмотря на приведенные выше замечания, по крайней мере,  вполне очевидно, что в кругах пользователей объектными языками программирования  термином  объект  во  всяком  случае  обозначается  то   понятие, которое с использованием более традиционной

терминологии следовало бы называть либо значением, либо переменной (а возможно, и оба эти понятия). Но, к сожалению, этот термин применяется также  специалистами в некоторых других областях, в частности, он встречается в определенных областях семантического моделирования в составе всевозможных  методов и методологий объектного анализа и проектирования или объектного моделирования (см., например, [14.3]). А в этих областях, по-видимому, данный  термин трактуется не как значение или переменная, а скорее как то, что в сообществе пользователей баз данных чаще всего принято называть сущностью  (кстати, под этим, кроме всего прочего, подразумевается, что, в отличие от объектов из языков программирования, объекты, называемые сущностями, определенно не инкапсулированы). Иными словами, так называемое объектное моделирование в действительности представляет собой просто моделирование сущностей/связей под другим названием; фактически в [14.3] в той или иной степени признается справедливость этого утверждения. Вследствие этого,  информационные структуры, выявленные с помощью этих методологий как объекты, затем (вполне обоснованно) отображаются на кортежи в переменных  отношения, а не на значения в доменах. При этом возникает невероятная путаница!

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

По теме:

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