Var fRejs: file of record kRejs{код рейса}:integer;
Gp{город прибытия (конец маршрута)}:STRING; Sost{сведения об остановках}: ARRAY[1..50]
OF {для каждой из 50-ти остановок} RECORD NamO{наименование остановки}:STRING;
TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING;
DlO{длительность проезда до неё}:REAL; DlSt{длительность стоянки}:INTEGER END;
DSv{детальные сведения}: ARRAY[1..52] OF {для каждой из 52-х недель текущего года} RECORD Spb{сведения о проданных билетах}: ARRAY[1..7]
OF {для каждого из 7-ми дней этой недели} RECORD Vot{время отправления}:STRING;
Svg{список вагонов}: ARRAY[1..10] OF {для каждого из 10 вагонов} RECORD
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING;
KolMest{количество мест в вагоне}}:INTEGER;
Zvg {заполненность вагона}: ARRAY[1.. 351]
OF {для каждого места вагона} RECORD FIO{ФИО пассажира}:STRING;
Npv{порядковый номер остановки - пункта высадки}:STRING END END;
FIOnp{ФИО начальника поезда}:STRING; Spvg{список проводников}: ARRAY[1..10]
OF {для каждого из 10 вагонов} STRING {ФИО проводника} END END END
Необходимо провести заданное отношение к 3-ей нормальной форме.
***************************************************************************
Изучая структуру записей файла можно сделать одно замечание: с одной стороны для каждого дня недели приведен список вагонов, для каждого из которых определен тип вагона и список пассажиров, купивших в него билеты, с другой стороны задается список проводников поезда. Можно предположить, что каждый проводник прикреплен к определенному вагону и соответственно скорректировать структуру записи. С учетом данного замечания структура записи может быть представлена в следующем виде:
Var fRejs: file of
RECORD
KRejs{код рейса}:INTEGER;
Gp{город прибытия (конец маршрута)}:STRING;
Sost{сведения об остановках}: ARRAY[1..50]OF {для каждой из 50-ти
остановок}
RECORD
NamO{наименование остановки}:STRING;
TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING;
DlO{длительность проезда до неё}:REAL;
DlSt{длительность стоянки}:INTEGER
END;
DSv{детальные сведения}: ARRAY[1..52] OF {для каждой из 52-х недель
текущего года}
RECORD Spb{сведения о проданных билетах}: ARRAY[1..7]OF {для каждого из
7-ми дней этой недели}
RECORD Vot{время отправления}:STRING;
Svg{список вагонов}: ARRAY[1..10] OF {для каждого из 10 вагонов}
RECORD
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING;
KolMest{количество мест в вагоне}}:INTEGER;
FioProv: {Фамилия проводника} STRING;
Zvg{заполненность вагона}: ARRAY[1.. 35]OF {для каждого из 35-ти мест вагона}
RECORD
FIO{ФИО пассажира}:STRING;
Npv{порядковый номер остановки - пункта высадки}:STRING
END
END
FIOnp{ФИО начальника поезда}:STRING;
END;
END
END;
Рассмотрим один из вариантов декомпозиции данной структуры в множество таблиц реляционной базы данных
Можно заметить, что в данном примере каждая запись файла описана древообразной структурой, корневой вершине которой приписан номер рейса. Ниже приведено полное дерево, описывающее эту структуру:
Номер рейса:
Пункт назначения
Сведения об остановках:
1-я остановка:
Наименование остановки
Тип остановки
Длительность проезда
Длительность стоянки
2-я остановка:
Наименование остановки
Тип остановки
Длительность проезда
Длительность стоянки
. . . . . . . . . . . . .
50-я остановка:
Наименование остановки
Тип остановки
Длительность проезда
Длительность стоянки
Номер недели:
1-я неделя:
Номер дня недели:
1 день:
Время отправления
Фамилия начальника поезда
Список вагонов:
1-ый вагон:
Тип вагона
Количество мест в вагоне
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место2:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . .
10-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . .
7 день:
Время отправления
Список вагонов:
1-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . .
10-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
52-я неделя:
День недели:
1 день:
Время отправления
Список вагонов:
1-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . .
10-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . .
7 день:
Время отправления
Список вагонов:
1-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . .
10-ый вагон:
Тип вагона
ФИО проводника
Количество проданных билетов
Места:
первое место:
ФИО пассажира
Порядковый номер остановки
второе место:
ФИО пассажира
Порядковый номер остановки
. . . . . . . . . . . . . . .
35-е место:
ФИО пассажира
Порядковый номер остановки
Для такой иерархической структуры достаточно легко находить функциональные зависимости между атрибутами исходного отношения. В частности, атрибут, приписанный некоторой вершине в дереве, функционально зависит от всех или части атрибутов расположенных на пути, ведущем от корня дерева к заданной вершине. В частности, в нашем случае можно описать зависимости:
(код рейса, номер остановки) -> наименование остановки;
(код рейса, номер остановки) -> тип остановки;
Вообще говоря, множество связей не ограничивается связями между атрибутами, описывающими вершины, находящиеся на одном пути от корня к листу, и может являться более широким. Например, количество мест в вагоне определяется типом вагона (естественно считать количество мест в купейном, плацкартном и общем вагоне разным). Поэтому можно говорить о зависимости: тип вагона -> количество мест, которое связывает вершины одного уровня.
Кроме того, нередко возникает необходимость объединения некоторых атрибутов, которое достигается определением новых атрибутов. Для данного примера, поскольку наименование остановки и тип остановки описывают один и тот же объект, можно ввести атрибут код остановки, который однозначно определяет как наименование, так и тип населенного пункта. После введения такого атрибута появляются дополнительные связи:
(код рейса, номер остановки) -> код остановки;
код остановки -> наименование остановки
код остановки -> тип остановки;
Приведенное представление не является единственным. Можно предложить различные преобразование, которое меняет структуру дерева с сохранением семантических связей. Например, любое дерево может быть свернуто в одноуровневое дерево, состоящее из единственной корневой вершины и листьев, причем каждый лист описывается некоторой сложной структурой. Для каждого листа в дереве можно построить вектор, который описывает метки пути, ведущего из корня дерева в соответствующий лист. Такого рода преобразования позволяют описать информацию о рейсах в виде одного отношения (таблицы). Нетрудно заметить, что такой способ описания не является хорошим: он содержит избыточную информацию (наименования одного и того же населенного пункта могут появляться многократно для различных рейсов, одни и те же проводники могут в разное время обслуживать различные поезда и т.д.); с другой стороны информация о количестве остановок, о заполнении мест в вагонах меняется от рейса к рейсу (к чему хранить информацию для места, на который не был продан билет), поэтому фиксированная структура таблицы может привести к большому количеству незаполненных клеток.
Рассмотрим приведение таблицы к 1NF.
Поскольку на первом уровне находятся три вершины, из которых только одна содержит атомарную информацию, а остальные содержат сложные атрибуты, произведем выделение этой информации в виде отдельных таблиц:
Шаг 1:
Таблица Rejs:
KodRejs: {Номер рейса} : INTEGER; |
Gp {город прибытия (конец маршрута)}:STRING; |
Таблица Ost:
KodRejs: (Номер рейса) : INTEGER; NomOst {Номер остановки} : INTEGER; |
NamO {наименование остановки}:STRING; TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING; DlO{длительность проезда до неё}:REAL; DlSt{длительность стоянки}:INTEGER; |
Для идентификации остановки введен атрибут NomOst, который характеризует порядковый номер остановки в рейсе.
Таблица DetSved:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; |
Sрb {сведения о проданных билетах}:структурный; |
Детальные сведения относятся к определенному дню года, каждый день кодируется сложным атрибутом (NomNed,NomDen). Поскольку таблица DetSved содержит сложный атрибут(Sрb), выделим подчиненное отношение, спускаясь на один уровень вниз описанного выше дерева:
Шаг 2:
Таблица DetSved:
KodRejs: (номер рейса) : INTEGER; NomNed {номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; |
Vot{время отправления}:STRING; FIOnp{ФИО начальника поезда}:STRING; Svg{список вагонов}: структурный |
Шаг 3:
Продолжая декомпозицию таблицы DetSved, получим:
Таблица DetSved:
KodRejs: (номер рейса) : INTEGER; NomNed {номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; |
Vot{время отправления}:STRING; FIOnp{ФИО начальника поезда}:STRING; |
Таблица Svg (список вагонов):
KodRejs: (номер рейса) : INTEGER; NomNed {номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; |
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING; KolMest (Количество мест) : INTEGER; FioProv: {Фамилия проводника} STRING; Zvg{заполненность вагона}: структурный |
Вводится атрибут номер вагона (NomVagon)
Шаг 4:
Поскольку таблица Sved_Vagon содержит сложные атрибуты, выделим подчиненное отношение, спускаясь на один уровень описанного выше дерева:
Таблица Svg:
KodRejs: (номер рейса) : INTEGER; NomNed {номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; |
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING; FioProv: {Фамилия проводника} STRING; |
Таблица Zvg (Заполненность вагона):
KodRejs: (номер рейса) : INTEGER; NomNed {номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; NomMest{номер места}: INTEGER; |
FIO{ФИО пассажира}:STRING; Npv{порядковый номер остановки – пункта высадки}:STRING |
Вводится атрибут номер места в вагоне (NomMest)
Шаг 4 завершает приведение таблицы к 1 нормальной форме. В результате получим базу данных, состоящую из 5 таблиц:
Таблица Rejs:
KodRejs: {Номер рейса} : INTEGER; |
Gp {город прибытия (конец маршрута)}:STRING; |
Таблица Ost:
KodRejs: (Номер рейса) : INTEGER; NomOst {Номер остановки} : INTEGER; |
NamO {наименование остановки}:STRING; TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING; DlO{длительность проезда до неё}:REAL; DlSt{длительность стоянки}:INTEGER; |
Таблица DetSved:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; |
Vot{время отправления}:STRING; FIOnp{ФИО начальника поезда}:STRING; |
Таблица Svg:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; |
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING; KolMest (Количество мест) : INTEGER; FioProv: {Фамилия проводника} STRING; |
Таблица Zvg:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; NomMest{номер места}: INTEGER; |
FIO{ФИО пассажира}:STRING; Npv{порядковый номер остановки - пункта высадки}:STRING |
Сведения об остановках встречаются в нескольких таблицах. Поскольку список остановок меняется от рейса к рейсу, то вполне резонно выделить этот список в отдельную таблицу. Введем в таблице Ost не ключевое поле KodO {код остановки}: INTEGER; (Заметим, что KodO не совпадает с атрибутом NomOst, поскольку атрибут KodO идентифицирует остановку, а атрибут NomOst является порядковым номером остановки для данного рейса);
Таблица Ost:
KodRejs: (Номер рейса) : INTEGER; NomOst {Номер остановки} : INTEGER; |
KodO {код остановки}: INTEGER; NamO {наименование остановки}:STRING; TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING; DlO{длительность проезда до неё}:REAL; DlSt{длительность стоянки}:INTEGER; |
Изучая функциональные зависимости в таблице Ost, получим следующие функциональные зависимости:
(KodRejs,NomOst) KodOst
(KodRejs,NomOst) NamO
(KodRejs,NomOst) TypO
KodOst NamO (неключевой атрибут функционально зависит от неключевого)
KodOst TypO
Согласно алгоритму приведения к третьей нормальной форме модифицированная таблица Ost разбивается на две:
Таблица Ost:
KodRejs: (Номер рейса) : INTEGER; NomOst {Номер остановки} : INTEGER; |
KodO {код остановки}: INTEGER; DlO{длительность проезда до неё}:REAL; DlSt{длительность стоянки}:INTEGER; |
Таблица SpOst:
KodO {код остановки}: INTEGER; |
NamO{наименование остановки}:STRING; TypO:{тип населенного пункта-остановки (областной центр, райцентр, город, поселок городского типа, поселок, станица, станция)}:STRING;
|
Можно также предположить, что проводники из рейса выбираются из некоторого общего списка. Добавим в таблицу Svg атрибут KodProv {Код проводника}: INTEGER;
Таблица Svg:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; |
Tvg{тип вагона (плацкарт, купе, мягкий)}:STRING; KolMest (Количество мест) : INTEGER; KodProv: {Код проводника} INTEGER; FioProv: {Фамилия проводника} STRING; |
Для данного отношения имеем следующие функциональные связи:
(KodRejs,NomNed,NomDen,NomVagon) KodProv
(KodRejs,NomNed,NomDen,NomVagon) FioProv
KodProv FioProv
Также заметим, что
(KodRejs,NomNed,NomDen,NomVagon) Tvg
(KodRejs,NomNed,NomDen,NomVagon) KolMest
Tvg KolMest
Согласно алгоритму приведения к третьей нормальной форме модифицированная таблица Svg разбивается на три:
Таблица Svg:
KodRejs: (Номер рейса) : INTEGER; NomNed {Номер недели} : INTEGER; NomDen {номер дня недели}: INTEGER; NomVagon {номер вагона}: INTEGER; |
Tvg{тип вагона (плацкарт, купе, мягкbй)}:STRING; KodProv: {Код проводника} INTEGER;
|
Таблица Prov:
KodProv: {Код проводника} INTEGER; |
FioProv: {Фамилия проводника} STRING; |
Таблица TVag:
Tvg: { тип вагона } INTEGER; |
KolMest (Количество мест) : INTEGER;
|
В результате декомпозиции поучается следующая схема БД:
Этот вариант декомпозиции использовал правила приведения к 1 и 3 нФ, поскольку результаты полученные при приведении к 1 НФ, автоматически имели форму 2НФ.
Рассмотрим несколько другой вариант декомпозиции таблицы DetSved. Сворачивая поддерево, описывающее это отношение можно получить следующую таблицу Zvg, которая находится в 1NF:
KodRejs: (Номер рейса) : INTEGER;
NomNed {Номер недели} : INTEGER;
NomDen {номер дня недели}: INTEGER;