Нормализация БД – дробление на подтаблицы (справочники) и определение связей — КиберПедия 

Папиллярные узоры пальцев рук - маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни...

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...

Нормализация БД – дробление на подтаблицы (справочники) и определение связей

2020-05-08 302
Нормализация БД – дробление на подтаблицы (справочники) и определение связей 0.00 из 5.00 0 оценок
Заказать работу


Наша текущая таблица Employees имеет недостаток в том, что в полях Position и Department пользователь может ввести любой текст, что в первую очередь чревато ошибками, так как он у одного сотрудника может указать в качестве отдела просто «ИТ», а у второго сотрудника, например, ввести «ИТ-отдел», у третьего «IT». В итоге будет непонятно, что имел ввиду пользователь, т.е. являются ли данные сотрудники работниками одного отдела, или же пользователь описался и это 3 разных отдела? А тем более, в этом случае, мы не сможем правильно сгруппировать данные для какого-то отчета, где, может требоваться показать количество сотрудников в разрезе каждого отдела.

Второй недостаток заключается в объеме хранения данной информации и ее дублированием, т.е. для каждого сотрудника указывается полное наименование отдела, что требует в БД места для хранения каждого символа из названия отдела.

Третий недостаток – сложность обновления данных полей, в случае если изменится название какой-то должности, например, если потребуется переименовать должность «Программист», на «Младший программист». В данном случае нам придется вносить изменения в каждую строчку таблицы, у которой Должность равняется «Программист».

Чтобы избежать данных недостатков и применяется, так называемая, нормализация базы данных – дробление ее на подтаблицы, таблицы справочники. Не обязательно лезть в дебри теории и изучать что из себя представляют нормальные формы, достаточно понимать суть нормализации.

Давайте создадим 2 таблицы справочники «Должности» и «Отделы», первую назовем Positions, а вторую соответственно Departments:

CREATE TABLE Positions(

ID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Positions PRIMARY KEY,

Name nvarchar(30) NOT NULL

)

 

CREATE TABLE Departments(

ID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Departments PRIMARY KEY,

Name nvarchar(30) NOT NULL

)


Заметим, что здесь мы использовали новую опцию IDENTITY, которая говорит о том, что данные в столбце ID будут нумероваться автоматически, начиная с 1, с шагом 1, т.е. при добавлении новых записей им последовательно будут присваиваться значения 1, 2, 3, и т.д. Такие поля обычно называют автоинкрементными. В таблице может быть определено только одно поле со свойством IDENTITY и обычно, но необязательно, такое поле является первичным ключом для данной таблицы.

На заметку
В разных СУБД реализация полей со счетчиком может делаться по своему. В MySQL, например, такое поле определяется при помощи опции AUTO_INCREMENT. В ORACLE и Firebird раньше данную функциональность можно было съэмулировать при помощи использования последовательностей (SEQUENCE). Но насколько я знаю в ORACLE сейчас добавили опцию GENERATED AS IDENTITY.

 

Давайте заполним эти таблицы автоматически, на основании текущих данных записанных в полях Position и Department таблицы Employees:

-- заполняем поле Name таблицы Positions, уникальными значениями из поля Position таблицы Employees

INSERT Positions(Name)

SELECT DISTINCT Position

FROM Employees

WHERE Position IS NOT NULL -- отбрасываем записи у которых позиция не указана


То же самое проделаем для таблицы Departments:

INSERT Departments(Name)

SELECT DISTINCT Department

FROM Employees

WHERE Department IS NOT NULL


Если теперь мы откроем таблицы Positions и Departments, то увидим пронумерованный набор значений по полю ID:

SELECT * FROM Positions

 

ID Name
1 Бухгалтер
2 Директор
3 Программист
4 Старший программист

 

SELECT * FROM Departments

 

ID Name
1 Администрация
2 Бухгалтерия
3 ИТ


Данные таблицы теперь и будут играть роль справочников для задания должностей и отделов. Теперь мы будем ссылаться на идентификаторы должностей и отделов. В первую очередь создадим новые поля в таблице Employees для хранения данных идентификаторов:

-- добавляем поле для ID должности

ALTER TABLE Employees ADD PositionID int

-- добавляем поле для ID отдела

ALTER TABLE Employees ADD DepartmentID int


Тип ссылочных полей должен быть каким же, как и в справочниках, в данном случае это int.

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

ALTER TABLE Employees ADD PositionID int, DepartmentID int


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

ALTER TABLE Employees ADD CONSTRAINT FK_Employees_PositionID

FOREIGN KEY(PositionID) REFERENCES Positions(ID)


И то же самое сделаем для второго поля:

ALTER TABLE Employees ADD CONSTRAINT FK_Employees_DepartmentID

FOREIGN KEY(DepartmentID) REFERENCES Departments(ID)


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

Имя ссылочного ограничения, обычно является составным, оно состоит из префикса «FK_», затем идет имя таблицы и после знака подчеркивания идет имя поля, которое ссылается на идентификатор таблицы-справочника.

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

Так же в некоторых случаях ссылку можно организовать по нескольким полям:

ALTER TABLE таблица ADD CONSTRAINT имя_ограничения

FOREIGN KEY(поле1,поле2,…) REFERENCES таблица_справочник(поле1,поле2,…)


В данном случае в таблице «таблица_справочник» первичный ключ представлен комбинацией из нескольких полей (поле1, поле2,…).

Собственно, теперь обновим поля PositionID и DepartmentID значениями ID из справочников. Воспользуемся для этой цели DML командой UPDATE:

UPDATE e

SET

PositionID=(SELECT ID FROM Positions WHERE Name=e.Position),

DepartmentID=(SELECT ID FROM Departments WHERE Name=e.Department)

FROM Employees e


Посмотрим, что получилось, выполнив запрос:

SELECT * FROM Employees

 

ID Name Birthday Email Position Department PositionID DepartmentID
1000 Иванов И.И. NULL NULL Директор Администрация 2 1
1001 Петров П.П. NULL NULL Программист ИТ 3 3
1002 Сидоров С.С. NULL NULL Бухгалтер Бухгалтерия 1 2
1003 Андреев А.А. NULL NULL Старший программист ИТ 4 3


Всё, поля PositionID и DepartmentID заполнены соответствующие должностям и отделам идентификаторами надобности в полях Position и Department в таблице Employees теперь нет, можно удалить эти поля:

ALTER TABLE Employees DROP COLUMN Position,Department


Теперь таблица у нас приобрела следующий вид:

SELECT * FROM Employees

 

ID Name Birthday Email PositionID DepartmentID
1000 Иванов И.И. NULL NULL 2 1
1001 Петров П.П. NULL NULL 3 3
1002 Сидоров С.С. NULL NULL 1 2
1003 Андреев А.А. NULL NULL 4 3


Т.е. мы в итоге избавились от хранения избыточной информации. Теперь, по номерам должности и отдела можем однозначно определить их названия, используя значения в таблицах-справочниках:

SELECT e.ID,e.Name,p.Name PositionName,d.Name DepartmentName

FROM Employees e

LEFT JOIN Departments d ON d.ID=e.DepartmentID

LEFT JOIN Positions p ON p.ID=e.PositionID

 

ID Name PositionName DepartmentName
1000 Иванов И.И. Директор Администрация
1001 Петров П.П. Программист ИТ
1002 Сидоров С.С. Бухгалтер Бухгалтерия
1003 Андреев А.А. Старший программист ИТ


В инспекторе объектов мы можем увидеть все объекты, созданные для в данной таблицы. Отсюда же можно производить разные манипуляции с данными объектами – например, переименовывать или удалять объекты.

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

ALTER TABLE Employees ADD ManagerID int


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

Теперь создадим FOREIGN KEY на таблицу Employees:

ALTER TABLE Employees ADD CONSTRAINT FK_Employees_ManagerID

FOREIGN KEY (ManagerID) REFERENCES Employees(ID)


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

В результате мы должны увидеть следующую картину (таблица Employees связана с таблицами Positions и Depertments, а так же ссылается сама на себя):

Напоследок стоит сказать, что ссылочные ключи могут включать дополнительные опции ON DELETE CASCADE и ON UPDATE CASCADE, которые говорят о том, как вести себя при удалении или обновлении записи, на которую есть ссылки в таблице-справочнике. Если эти опции не указаны, то мы не можем изменить ID в таблице справочнике у той записи, на которую есть ссылки из другой таблицы, так же мы не сможем удалить такую запись из справочника, пока не удалим все строки, ссылающиеся на эту запись или, же обновим в этих строках ссылки на другое значение.

Для примера пересоздадим таблицу с указанием опции ON DELETE CASCADE для FK_Employees_DepartmentID:

DROP TABLE Employees

 

CREATE TABLE Employees(

ID int NOT NULL,

Name nvarchar(30),

Birthday date,

Email nvarchar(30),

PositionID int,

DepartmentID int,

ManagerID int,

CONSTRAINT PK_Employees PRIMARY KEY (ID),

CONSTRAINT FK_Employees_DepartmentID FOREIGN KEY(DepartmentID) REFERENCES Departments(ID)

ON DELETE CASCADE,

CONSTRAINT FK_Employees_PositionID FOREIGN KEY(PositionID) REFERENCES Positions(ID),

CONSTRAINT FK_Employees_ManagerID FOREIGN KEY (ManagerID) REFERENCES Employees(ID)

)

 

INSERT Employees (ID,Name,Birthday,PositionID,DepartmentID,ManagerID)VALUES

(1000,N'Иванов И.И.','19550219',2,1,NULL),

(1001,N'Петров П.П.','19831203',3,3,1003),

(1002,N'Сидоров С.С.','19760607',1,2,1000),

(1003,N'Андреев А.А.','19820417',4,3,1000)


Удалим отдел с идентификатором 3 из таблицы Departments:

DELETE Departments WHERE ID=3


Посмотрим на данные таблицы Employees:

SELECT * FROM Employees

 

ID Name Birthday Email PositionID DepartmentID ManagerID
1000 Иванов И.И. 1955-02-19 NULL 2 1 NULL
1002 Сидоров С.С. 1976-06-07 NULL 1 2 1000


Как видим, данные по отделу 3 из таблицы Employees так же удалились.

Опция ON UPDATE CASCADE ведет себя аналогично, но действует она при обновлении значения ID в справочнике. Например, если мы поменяем ID должности в справочнике должностей, то в этом случае будет производиться обновление DepartmentID в таблице Employees на новое значение ID которое мы задали в справочнике. Но в данном случае это продемонстрировать просто не получится, т.к. у колонки ID в таблице Departments стоит опция IDENTITY, которая не позволит нам выполнить следующий запрос (сменить идентификатор отдела 3 на 30):

UPDATE Departments

SET

ID=30

WHERE ID=3


Главное понять суть этих 2-х опций ON DELETE CASCADE и ON UPDATE CASCADE. Я применяю эти опции очень в редких случаях и рекомендую хорошо подумать, прежде чем указывать их в ссылочном ограничении, т.к. при нечаянном удалении записи из таблицы справочника это может привести к большим проблемам и создать цепную реакцию.

Восстановим отдел 3:

-- даем разрешение на добавление/изменение IDENTITY значения

SET IDENTITY_INSERT Departments ON

 

INSERT Departments(ID,Name) VALUES(3,N'ИТ')

 

-- запрещаем добавление/изменение IDENTITY значения

SET IDENTITY_INSERT Departments OFF


Полностью очистим таблицу Employees при помощи команды TRUNCATE TABLE:

TRUNCATE TABLE Employees


И снова перезальем в нее данные используя предыдущую команду INSERT:

INSERT Employees (ID,Name,Birthday,PositionID,DepartmentID,ManagerID)VALUES

(1000,N'Иванов И.И.','19550219',2,1,NULL),

(1001,N'Петров П.П.','19831203',3,3,1003),

(1002,N'Сидоров С.С.','19760607',1,2,1000),

(1003,N'Андреев А.А.','19820417',4,3,1000)

 

Подытожим


На данным момент к нашим знаниям добавилось еще несколько команд DDL:

· Добавление свойства IDENTITY к полю – позволяет сделать это поле автоматически заполняемым (полем-счетчиком) для таблицы;

· ALTER TABLE имя_таблицы ADD перечень_полей_с_характеристиками – позволяет добавить новые поля в таблицу;

· ALTER TABLE имя_таблицы DROP COLUMN перечень_полей – позволяет удалить поля из таблицы;

· ALTER TABLE имя_таблицы ADD CONSTRAINT имя_ограничения FOREIGN KEY (поля) REFERENCES таблица_справочник(поля) – позволяет определить связь между таблицей и таблицей справочником.

 


Поделиться с друзьями:

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

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

История развития пистолетов-пулеметов: Предпосылкой для возникновения пистолетов-пулеметов послужила давняя тенденция тяготения винтовок...

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.055 с.