Об одном языке манипулирования данными
|
FIXEDPOINT(<имя
продукции>) |
Истина, если после очередного вычисления указанной продукции не было
зафиксировано изменений в таблицах, соответствующих предикатам заключения
этой продукции |
EMPTY(<имя
предиката>) |
Истина, если таблица пуста |
HAVE(<имя
предиката>, <строка аргументов>) |
Истина, если соответствующая таблица содержит указанную строку
аргументов |
Для непосредственной работы с таблицами используются так называемые директивные
операторы, предназначенные для варьирования содержимого таблиц символических
предикатов. Они позволяют изменять, очищать, копировать и перемещать строки из
соответствующих таблиц.
В зависимости от выполняемого действия директивный оператор записывается
в одном из следующих двух шаблонов:
(1)
<действие1> < место_действия1> IN <предикат1>
TO
<место_действия2> IN <предикат2>
(2)
<действие2> <место_действия1> IN
<предикат1>
При этом,
действие1 ::= MOVE
| COPY,
действие2 ::= ERASE|CUT|PASTE|ADD –
определяют выполняемое
действие (перемещение, копирование, очищение, вырезка в буферную таблицу,
вставка из буферной таблицы и добавление строк соответственно).
место_действия
::= FIRSTLINE|LASTLINE|ALL|
FROM <разделитель> UPTO <разделитель> |
FROM <разделитель> DOWNTO <разделитель> |
MATCH <строка аргументов>|
NOT MATCH<строка
аргументов> | BEFORE
<разделитель> |
AFTER <разделитель>| BEFORE <строка аргументов> -
определяет строки, над которыми
выполняется действие. Для разных действий множество допустимых мест
действия различно. Семантика определения места действия и допустимые
конфигурации директивного оператора описаны дальше в таблицах.
Описание места действия
директивного оператора
Синтаксис |
Сокращенное обозначение |
Семантика, в зависимости |
|
Таблица-источник |
Таблица-приемник |
||
FIRSTLINE |
FL |
Первая строка |
Вместо первой строки |
LASTLINE |
LL |
Последняя строка |
Вместо последней строки |
ALL |
AL |
Все строки |
Вместо всех строк |
FROM DOWNTO |
DN |
От первого разделителя до второго сверху вниз |
Вместо строк от первого разделителя до второго сверху вниз |
FROM UPTO |
UP |
От первого разделителя до второго снизу вверх |
Вместо строк от первого разделителя до второго снизу вверх |
MATCH (строка аргументов) |
MA |
Строки, совпадающие с маской |
Вместо совпадающих с маской строк |
NOT MATCH (строка аргументов)
|
NM |
Не совпадающие с маской строки |
Вместо несовпадающих с маской строк |
Осуществляемые действия и
способы указания места в таблицах позволяют вырезать, копировать и перемещать
строки из таблицы в таблицу и из таблицы в буфер и обратно. Выполнение этого
оператора возможно также для части таблицы, удовлетворяющей определенному
условию. Если действие директивного оператора затрагивает сразу несколько
таблиц, то без особых оговорок его можно выполнять только над таблицами с
одинаковым числом столбцов (т.е. одинаковой ширины).
Допустимые конфигурации
операторов вида (1).
Действие |
Допустимое место_действия1 |
Допустимое место_действия2 |
COPY, |
FL, LL |
FL, LL, AL, UP, DN, MA, NM, BE, AF |
MOVE |
AL, UP, DN, MA, NM, BE, AF |
AL, UP, DN, MA, NM, BE, AF |
Допустимые конфигурации
операторов вида (2).
Действие |
Допустимое место_действия1 |
ERASE, CUT |
FL, LL, AL, UP,
DN, MA, NM, BE, AF |
PASTE, ADD |
AL, UP, DN, MA,
NM, BE, AF |
Строка аргументов в директивном операторе состоит из констант и символа
“_” (подчеркивание). Символ “_” определяет замещаемые (немаскируемые) места в
маске.
Действия директивных
операторов следующие:
ADD <строка> |
Добавляет новую строку <строка>,
описанную на языке описания предикатов, в таблицу, над которой осуществляется
действие. Добавляемая строка не может содержать переменных и иметь не
соответствующее ширине таблицы количество элементов. Например, оператор ADD (s1.NAME1, s2.NAME2) BEFORE_START IN
pred1 добавляет
строку s1.NAME1, s2.NAME2 в начало таблицы pred1. |
ERASE |
Удаляет указываемый фрагмент таблицы. Например,
оператор ERASE ALL IN bit удалит все строки из таблицы bit. |
COPY |
Копирует указываемый фрагмент одной таблицы в другую
таблицу. Например, оператор COPY ON <условие1> MATCH IN
пред1 ТО ALL IN пред2 копирует строки, удовлетворяющих
условию условие1 из таблицы пред1 в
конец таблицы пред2. Зарезервированное слово BUFFER
в качестве имени указывает, что копировать фрагмент требуется в буфер.
Например, оператор COPY LASTLINE IN pred1 TO BUFFER скопирует в буфер последнюю строку предиката pred1.
Если таблица pred1 пуста, содержимым буфера будет пустая
таблица такой же ширины, что и таблица pred1. Для
копирования содержимого одной таблицы в другую используется оператор COPY ALL IN pred1 TO pred2 Он скопирует все строки таблицы pred1 в таблицу pred2 |
MOVE |
Перемещает указываемый фрагмент таблицы и добавляет в таблицу с
именем <имя1>. |
Выделение места
действия в директивных операторах осуществляется одним из следующих
способов:
ALL |
Указывает всю таблицу, с первой по последнюю строку.
В рассматриваемом примере директива ERASE IN Tabl
ALL очищает
таблицу Tabl. |
FROM a1 TO a2 |
Указывает фрагмент таблицы от разделителя a1 до
разделителя a2. Способ установки и работа с разделителями
описаны ниже. Вместо a1 можно поставить TOP, т.е. 1, вместо
а2 - BOTTOM, т.е.
номер ее последней записи |
FIRSTLINE
и LASTLINE |
Указывают в качестве места действия оператора первую и последнюю
строки таблицы соответственно |
LINE_BEFORE а1 и LINE_AFTER а1 |
Указывают в качестве места действия оператора единственную строку предиката соответственно перед
разделителем и после разделителя а1 |
Пример 9. Пусть исходная таблица pred имеет следующий
вид:
(S.A, S.B,
S.C)
(S.A, S.С, S.C)
(S.A, S.D,
S.C)
(S.A, S.E,
S.A)
(S.A, S.F,
S.A)
Применение директивного
оператора
ERASE IN pred ON pred(_,S.D,_)
MATCH
изменит эту таблицу на
(S.A, S.B,
S.C)
(S.A, S.С,
S.C)
(S.A, S.E,
S.A)
(S.A, S.F,
S.A)
т.е. будет
удалена третья строка.
Оператор
ERASE IN pred ON pred (S.A, _, _) MATCH
очистит таблицу целиком,
так как в каждой строке на первом месте расположена константа S.A.
Продуктивные операторы осуществляют выполнение
продукций. Все они построены по следующей
схеме:
RUN <имя_продукции> <тип выполнения> <место
выполнения>.
Тип выполнения задает, как
выполняется продукция - один раз или несколько. Типы выполнения описаны в
следующей таблице.
UNTIL_FIX <предикат> |
Выполнять до достижения таблицей
неподвижной точки, соответствующей указанному предикату. |
ONCE |
Выполнить один раз. По умолчанию,
если после имени продукции не стоит ONCE, она также выполняется один раз |
FOR a1 |
Выполнить один раз для строки, на которую указывает
разделитель а1 |
DEEPTO имя_предиката (строка) |
Выполнить в глубину, до достижения
указанной строки или построения неподвижных точек всех предикатов, перечисленных
в заключениях продукции |
DEEP число |
Выполнить в глубину, число раз
или до построения неподвижных точек всех таблиц, перечисленных в заключениях
продукции |
Место выполнения
указывает для каждой таблицы тот фрагмент, который унифицируется с
соответствующим предикатом из ключей продукции. Эта часть продуктивного
оператора имеет такой же вид, как соответствующая часть для директивного
оператора, т.е. используются конструкции вида
IN имя_
таблицы место_в_таблице.
Из директив, указывающих
место действия, используются директивы типа FROM a1 TO
a2, где а1 и а2 суть
разделители, FIRSTLINE и LASTLINE. Тем самым указывается фрагмент
таблицы либо от разделителя а1 до разделителя а2,
либо первая, или последняя записи таблицы. Вместо а1 можно использовать
TOP,
т.е. указание на первую запись таблицы, вместо а2 - BOTTOM, т.е. номер ее последней записи.
По умолчанию предполагается, что место действия есть вся таблица.
4.
Примеры запросов к БД. Приведем
примеры моделирования средствами предлагаемого языка наиболее распространенных
операторов языка SQL. Как показывает опыт, такая реализация запросов нагляднее,
чем на стандартном SQL. При кажущейся громоздкости программ они прозрачны
с содержательной точки зрения и поэтому легко воспринимаются. Помимо этого язык
обладает рядом свойств, которые позволяют говорить о его большей выразительной
возможности, чем стандартный SQL. Поэтому реализация СУБД на принципах логического
моделирования является интересной альтернативой общепринятому подходу.
Чтобы лучше
ориентироваться в запросах, приведем схему БД, к которой они относятся (рис.
1).
Рис. 1
Пример 10.
Моделирование оператора Max
Запрос
на SQL:
SELECT MAX(Выход)
FROM Блюда
Предлагаемая программа на языке:
NAME: prMax
KEYS: ВремБлюдо(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, INT.F)
Блюдо(&INT.A1,
&Блюда.B1, &В.C1, &Основа.D1, &INT.E1, &INT.F1)
COND:
(&INT.E > &INT.E1)
CONC: NOT Блюдо(&INT.A1,&Блюда.B1,&В.C1,&Основа.D1,&INT.E1, &INT.F1)
COM: Моделирование оператора MAX. ВремБлюдо –
вспомогательная таблица. Ее аргументы упорядочены так же, как в таблице Блюдо
END
BEGIN
L1: MOVE FIRSTLINE IN Блюдо TO ALL
IN ВремБлюдо
RUN
prMax
IF
EMPTY(Блюдо) THEN GOTO L2
ELSE GOTO L1
L2: PRINT ВремБлюдо
END.
Пример 11.
Моделирование оператора Count
Запрос на SQL:
SELECT COUNT (БЛ)
FROM Блюдо
Предлагаемая программа:
NAME: prCount
KEYS: ВремБлюдо(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, &INT.F) SummCount(&INT.U)
COND:
CONC:
SummCount(&INT.U + INT.1) NOT SummCount(&INT.U)
COM: Моделирование оператора Count
END
BEGIN
L1: MOVE FIRSTLINE IN Блюдо TO ALL
IN ВремБлюдо
RUN
prCount
IF EMPTY(Блюдо) THEN
GOTO L2 ELSE GOTO L1
L2: PRINT SummCount
END.
Пример 12. Моделирование оператора GROUP BY. Требуется вычислить общую массу каждого из продуктов, поставляемых в
настоящее время поставщиками.
SELECT Основа, SUM(Выход) AS SUM
FROM Блюдо
GROUP BY Основа
Предлагаемая программа:
NAME: prОсноваСумм1
KEYS:
ВремБлюдо(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, &INT.F)
COND:
CONC: ОсноваСумм(&Основа.D, INT.0)
COM: Сумма инициализируется значением 0.
END
NAME: prОсноваСумм2
KEYS: SummWieght(&INT.A) ОсноваСумм(&Основа.X, INT.0)
COND:
CONC: ОсноваСумм(&Основа.X, &INT.A)
NOT ОсноваСумм(&Основа.X, INT.0)
COM:
END
NAME: prGroup_by
KEYS: Блюдо(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, &INT.F)
ВремБлюдо(&INT.A1, &Блюда.B1, &В.C1, &Основа.D, &INT.E1, &INT.F1)
COND:
CONC: ВремБлюдоAVR(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, &INT.F) NOT
Блюдо(&INT.A, &Блюда.B, &В.C, &Основа.D, &INT.E, &INT.F)
COM: Моделирование оператора GROUP BY. Таблица ВремБлюдо содержит одну запись,
поэтому в таблице ВремБлюдоAVR включены все записи с одним признаком Основа.
END
NAME: prSummaWieght
KEYS: ВремБлюдо(&INT.A1, &Блюда.B1, &В.C1, &Основа.D, &INT.E1, &INT.F1)
ОсноваСумм(&Основа.D, &INT.S0)
COND:
CONC: ОсноваСумм(&Основа.D, &INT.E + &INT.S0)
NOT
ОсноваСумм(&Основа.D, &INT.S0)
COM: Суммирует
выход продукта в одной группе.
END
BEGIN
ERASE ALL IN
ВремБлюдоAVR
ERASE ALL IN
ОсноваСумм
L1: COPY FIRSTLINE IN Блюдо TO ALL
IN ВремБлюдо
RUN
prGroup_by
COPY FIRSTLINE IN ВремБлюдоAVR
TO ALL IN ВремБлюдо
RUN
prОсноваСумм1
L2: MOVE FIRSTLINE IN ВремБлюдоAVR TO ALL IN ВремБлюдо
RUN prSummaWeight
ERASE ALL IN ВремБлюдо
/* суммирование в пределах одной группы Основа */
IF EMPTY(Блюдо) THEN
GOTO LE ELSE GOTO L1
LE: PRINT
ОсноваСумм
END.
Пример 13. Найти
поставщиков помидоров, которые в БД являются продуктом с номером 11.
SELECT Название,
Статус
FROM Поставщики
WHERE ПС IN
(SELECT
ПС
FROM Поставки
WHERE ПР IN
(SELECT ПР
FROM
Продукты
WHERE
Продукт = 'Помидоры'));
Предлагаемая программа:
NAME: pr_Temp_PR_0
KEYS: Продукты(&ПР.A, Продукт.Помидоры, &Бел._,
&Жир._, &Угл._, &K._, &Ca._, &Na._, &B2._, &PP._, &C._) Поставки(&ПС.A2, &ПР.A, &INT.C4, &INT._)
Поставщики(&ПС.A2, &Название.B3, &Статус.D3, &Город.E3, &Адрес.F3, &Тел.G3)
COND:
CONC: FINTabl(&Название.B3, &Статус.D3)
COM:
END
RUN
pr_Temp_PR_0
PRINT FINTabl
END.
Пример 14.
Выдать номера поставщиков, находящихся в том же городе, что и поставщик с
номером 6.
SELECT ПС
FROM Поставщики
WHERE Город =
(SELECT Город
FROM
Поставщики
WHERE
ПС = 6 );
Предлагаемая программа:
NAME: pr_Town
KEYS: Поставщики(ПС.6, &Название.B,
&Статус.C, &Город.D, &Адрес.E, &Тел.F)
COND:
CONC: TempTown(&Город.D)
COM:
END
NAME:
prFIN
KEYS: Поставщики(&ПС.A,&Название.B,&Статус.C,&Город.D,&Адрес.E, &Тел.F) TempTown(&Город.D)
COND:
CONC: FINPS(&ПС.A)
COM:
END
Пример 15. Выдать название и статус поставщиков
продукта с номером 11.
SELECT Название, Статус
FROM Поставщики
WHERE 11 IN
(SELECT ПР
FROM
Поставки
WHERE
ПС = Поставщики.ПС );
Предлагаемая программа:
1-й вариант.
NAME: pr_Temp_PR
KEYS: TempPostavchik(&ПС.A, &Название.B, &Статус.C, &Город.D, &Адрес.E, &Тел.F)
Поставки(&ПС.A, &ПР.B1, &INT.C1, &INT.D1)
COND:
CONC:
TempPR(&ПР.B1)
COM:
END
NAME: pr_Temp_PR1
KEYS:
TempPostavchik(&ПС.A,
&Название.B,
&Статус.C,
&Город.D,
&Адрес.E,
&Тел.F) TempPR(ПР.11)
COND:
CONC: FINTabl(&Название.B, &Статус.C)
COM:
END
BEGIN
ERASE ALL IN
FINTabl
L2: ERASE ALL IN TempPR
MOVE FIRSTLINE IN Поставщики TO ALL IN TempPostavchik
RUN
pr_Temp_PR
PRINT
TempPR
RUN
pr_Temp_PR1
IF EMPTY(Поставщики) THEN GOTO L3 ELSE GOTO
L2
L3: PRINT FINTabl
END.
2-й вариант.
NAME: pr_Temp_PR
KEYS:
TempPostavchik(&ПС.A,
&Название.B,
&Статус.C,
&Город.D,
&Адрес.E,
&Тел.F) Поставки(&ПС.A,ПР.11,&INT.C1,&INT.D1)
COND:
CONC: FINTabl(&Название.B, &Статус.C)
COM: Из поставщиков название и статус с признаком ПР = 11
BEGIN
ERASE ALL IN
FINTabl
L2: ERASE ALL IN TempPR
MOVE FIRSTLINE IN Поставщики TO ALL IN TempPostavchik
RUN
pr_Temp_PR
IF EMPTY(Поставщики) THEN GOTO L3 ELSE GOTO
L2
L3: PRINT FINTabl
END.
Пример 16. Выдать номера всех продуктов, поставляемых
только одним поставщиком.
SELECT DISTINCT
X.ПР
FROM Поставки X
WHERE X.ПР NOT IN
(SELECT
Y.ПР
FROM Поставки Y
WHERE Y.ПС <> X.ПС);
Предлагаемая программа:
NAME: pr_Temp_PR_0
KEYS:
TempPostavki(&ПС.A,
&ПР.B,
&INT.C, &INT.D)
MidPostavki(&ПС.A1, &ПР.B1, &INT.C1, &INT.D1)
COND:
(&ПС.A
!= &ПС.A1)
CONC: TempPR(&ПР.B1)
COM: выделяем все продукты, которые не
поставляет поставщик &ПС.А. Таблица TempPostavki сдержит одну запись, а MidPostavki – все записи из таблицы
Поставки.
END
NAME: pr_Temp_PR_1
KEYS:
TempPostavki(&ПС.A,
&ПР.B,
&INT.C, &INT.D) TempPR(&ПР.B)
COND:
CONC:
Flag(INT.1) NOT Flag(INT.0)
COM: если среди продуктов, поставляемых другими
поставщиками (не поставщиком &ПС.А) есть продукт, поставляемый поставщиком
&ПС.А, то это фиксируется признаком Flag(INT.1).
NAME: pr_FIN
KEYS:
TempPostavki(&ПС.A,
&ПР.B,
&INT.C, &INT.D) Flag(&INT.X)
COND:
(&INT.X = INT.0)
CONC: FINTabl(&ПР.B)
COND:
(&INT.X = INT.1)
CONC: NOT Flag(&INT.X) Flag(INT.0)
COM: Если продукт &ПР.В поставляется только
поставщиком &ПС.А (чему соответствует Flag(INT.0)), то
заносим его в окончательную таблицу FINTabl. Если он поставляется и другими
поставщиками (чему соответствует Flag(INT.1)), то не заносим.
END
BEGIN
ERASE ALL IN
FINTabl
COPY ALL IN
Поставки TO ALL IN MidPostavki
L1: ERASE ALL IN TempPR
MOVE FIRSTLINE IN Поставки TO ALL IN TempPostavki
RUN
pr_Temp_PR_0
RUN
pr_Temp_PR_1
RUN
pr_FIN
IF EMPTY(Поставки) THEN
PRINT FINTabl ELSE GOTO L1
END.
Пример 17. Выдать названия поставщиков, поставляющих продукт с номером
11.
SELECT Название
FROM Поставщики
WHERE EXISTS
(SELECT *
FROM Поставки
WHERE ПС = Поставщики.ПС AND ПР =
11 );
Предлагаемая программа:
NAME: pr_Temp_PR_0
KEYS:
TempPostavchik(&ПС.A,
&Название.B,
&Статус.C,
&Город.D,
&Адрес.E,
&Тел.F) Поставки(&ПС.A, ПР.11, &INT.C1, &INT.D1)
COND:
CONC: FINTabl(&Название.B, &Статус.C)
COM:
END
BEGIN
ERASE ALL IN
FINTabl
L1: MOVE FIRSTLINE IN Поставщики TO ALL
IN TempPostavchik
RUN
pr_Temp_PR_0
IF EMPTY(Поставщики) THEN GOTO L2 ELSE GOTO
L1
PRINT FINTabl
END.
Пример 18. Выдать название и статус
поставщиков, не поставляющих продукт с номером 11.
SELECT Название, Статус
FROM Поставщики
WHERE NOT
EXISTS
(SELECT
*
FROM Поставки
WHERE ПС = Поставщики.ПС AND ПР =
11);
Предлагаемая программа:
NAME:
pr_Temp_PR_0
KEYS:
TempPostavchik(&ПС.A,
&Название.B,
&Статус.C,
&Город.D,
&Адрес.E,
&Тел.F) Поставки(&ПС.A, ПР.11, &INT.C1, &INT.D1)
COND:
CONC:
TempPS(&ПС.А)
COM: Выделяем всех поставщиков,
поставляющих продукт ПР.11.
END
NAME: pr_Temp_PR_1
KEYS:
TempPostavchik(&ПС.A,
&Название.B,
&Статус.C,
&Город.D,
&Адрес.E,
&Тел.F)
COND:
CONC: FINTabl(&Название.B, &Статус.C)
COM: Выделяем название и статус
поставщиков, не поставляющих помидоры (ПР.11)
END
BEGIN
ERASE ALL IN
FINTabl
L1: ERASE ALL IN TempPS
MOVE FIRSTLINE IN Поставщики TO ALL IN TempPostavchik
RUN
pr_Temp_PR_0
IF
EMPTY(TempPS) THEN RUN pr_Temp_PR_1 ELSE GOTO L1
IF EMPTY(Поставщики) THEN GOTO L3 ELSE GOTO
L1
L3: PRINT FINTabl
END.
Пример 19. Выдать поставщиков
продуктов для летнего салата, которые поставляют эти продукты по минимальной
цене.
SELECT Продукт, Цена, Название, Статус
FROM Продукты, Состав, Блюда, Поставки, Поставщики
WHERE Продукты.ПР = Состав.ПР AND Состав.БЛ = Блюда.БЛ
AND Поставки.ПР = Состав.ПР AND Поставки.ПС = Поставщики.ПС
AND Блюда = 'Салат летний' AND Цена =
(SELECT MIN (Цена)
FROM Поставки X
WHERE X.ПР = Поставки.ПР );
Предлагаемая программа:
NAME: pr_Sel
KEYS: Блюдо(&БЛ.A, Блюда.Салат_летний, &B.С, &Основа.D, &INT.E, &INT.F) Состав(&ПС.A2, &ПР.B1, &INT.C2, &INT.D2)
COND:
CONC: Stoim(&ПР.В1, &INT.C2, &ПС.А2)
COM: По БЛ выбираем из состава необходимые коды продуктов
(ПР), добавляя их стоимость и код поставщика.
END
NAME: pr_Minstoim
KEYS: Stoim(&ПР.A, &INT.B, &ПС.C) Stoim(&ПР.A, &INT.B1, &ПС.C1)
COND: (&INT.B1 > &INT.B)
CONC: NOT Stoim(&ПР.А, &INT.B1, &ПС.С1)
COM: Выбираем продукты с минимальной стоимостью
END
NAME: pr_Temp_PR_0
KEYS: Stoim(&ПР.A, &INT.B, &ПС.C) Поставщики(&ПС.С1, &Название.В1, &Статус.С5, &Город.D1, &Адрес.Е1, &Тел.F1)
Продукты(&ПР.A, &Продукт.B3, &Бел.C3, &Жир.D2, &Угл.E2,
&K.F2, &Ca.D3, &Na.E3, &B2.F3, &PP.F4, &C.F5)
COND:
CONC: NOT
FINTabl(&Продукт.B3, &INT.B, &Название.В1, &Статус.С5,)
COM: Выбираем по коду поставщика название и статус, а по
коду продукта – его название (поле «Продукт»).
END
BEGIN
ERASE ALL IN
Stoim
RUN pr_Sel
RUN pr_Minstoim
RUN pr_Temp_PR_0
PRINT Stoim
PRINT FINTable
END.
Литература
1. Брошкова Н.Л.
Единая среда проектирования и эксплуатации информационной системы, основанная
на формально-семантическом подходе. Магистерская диссертация, М.: МАДИ, 2005.
155 с.
2. Попов С.В.
Логическое моделирование, М.: Тровант, 2006. 256 с.
3. Мартин
Дж. Организация баз данных в вычислительных системах. М.: Мир, 1980. 662
с.
4.
Дейт К. Введение в системы баз данных. М.: Наука, 1980.
5.
Мейер Д. Теория реляционных баз данных. М.: Мир, 1987. 608 с.
6. Мартен
Д. Базы данных: практические методы. М.: Радио и связь, 1983. 168 с.