INSERT Employees(ID,Position,Department,Name) VALUES

(1000,N'Директор',N'Администрация',N'Иванов И.И.'),

(1001,N'Программист',N'ИТ',N'Петров П.П.'),

(1002,N'Бухгалтер',N'Бухгалтерия',N'Сидоров С.С.'),

(1003,N'Старший программист',N'ИТ',N'Андреев А.А.')


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


CREATE TABLE Employees(

ID int NOT NULL CONSTRAINT PK_Employees PRIMARY KEY, -- указываем как характеристику поля

Name nvarchar(30) NOT NULL,

Birthday date,

Email nvarchar(30),

Position nvarchar(30),

Department nvarchar(30)

)


На самом деле имя ограничения можно и не задавать, в этом случае ему будет присвоено системное имя (наподобие «PK__Employee__3214EC278DA42077»):


CREATE TABLE Employees(

ID int NOT NULL,

Name nvarchar(30) NOT NULL,

Birthday date,

Email nvarchar(30),

Position nvarchar(30),

Department nvarchar(30),

PRIMARY KEY(ID)

)


Или:


CREATE TABLE Employees(

ID int NOT NULL PRIMARY KEY,

Name nvarchar(30) NOT NULL,

Birthday date,

Email nvarchar(30),

Position nvarchar(30),

Department nvarchar(30)

)


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


ALTER TABLE Employees DROP CONSTRAINT PK_Employees


Но такой краткий синтаксис, без указания имен ограничений, удобно применять при создании временных таблиц БД (имя временной таблицы начинается с # или ##), которые после использования будут удалены.

Подытожим


На данный момент мы рассмотрели следующие команды:

 

· CREATE TABLE имя_таблицы (перечисление полей и их типов, ограничений) – служит для создания новой таблицы в текущей БД;

· DROP TABLE имя_таблицы – служит для удаления таблицы из текущей БД;

· ALTER TABLE имя_таблицы ALTER COLUMN имя_столбца … – служит для обновления типа столбца или для изменения его настроек (например для задания характеристики NULL или NOT NULL);

· ALTER TABLE имя_таблицы ADD CONSTRAINT имя_ограничения PRIMARY KEY(поле1, поле2,…) – добавление первичного ключа к уже существующей таблице;

· ALTER TABLE имя_таблицы DROP CONSTRAINT имя_ограничения – удаление ограничения из таблицы.

 

Немного про временные таблицы

 

Вырезка из MSDN. В MS SQL Server существует два вида временных таблиц: локальные (#) и глобальные (##). Локальные временные таблицы видны только их создателям до завершения сеанса соединения с экземпляром SQL Server, как только они впервые созданы. Локальные временные таблицы автоматически удаляются после отключения пользователя от экземпляра SQL Server. Глобальные временные таблицы видны всем пользователям в течение любых сеансов соединения после создания этих таблиц и удаляются, когда все пользователи, ссылающиеся на эти таблицы, отключаются от экземпляра SQL Server.


Временные таблицы создаются в системной базе tempdb, т.е. создавая их мы не засоряем основную базу, в остальном же временные таблицы полностью идентичны обычным таблицам, их так же можно удалить при помощи команды DROP TABLE. Чаще используются локальные (#) временные таблицы.

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


CREATE TABLE #Temp(

ID int,

Name nvarchar(30)

)


Так как временная таблица в MS SQL аналогична обычной таблице, ее соответственно так же можно удалить самому командой DROP TABLE:


DROP TABLE #Temp

 

Так же временную таблицу (как собственно и обычную таблицу) можно создать и сразу заполнить данными возвращаемые запросом используя синтаксис SELECT … INTO:


SELECT ID,Name

INTO #Temp

FROM Employees

 

На заметку
В разных СУБД реализация временных таблиц может отличаться. Например, в СУБД ORACLE и Firebird структура временных таблиц должна быть определена заранее командой CREATE GLOBAL TEMPORARY TABLE с указанием специфики хранения в ней данных, дальше уже пользователь видит ее среди основных таблиц и работает с ней как с обычной таблицей.

 

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


Наша текущая таблица 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
Бухгалтер
Директор
Программист
Старший программист

 


SELECT * FROM Departments

 

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


Данные таблицы теперь и будут играть роль справочников для задания должностей и отделов. Теперь мы будем ссылаться на идентификаторы должностей и отделов. В первую очередь создадим новые поля в таблице 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
Иванов И.И. NULL NULL Директор Администрация
Петров П.П. NULL NULL Программист ИТ
Сидоров С.С. NULL NULL Бухгалтер Бухгалтерия
Андреев А.А. NULL NULL Старший программист ИТ


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


ALTER TABLE Employees DROP COLUMN Position,Department


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


SELECT * FROM Employees

 

ID Name Birthday Email PositionID DepartmentID
Иванов И.И. NULL NULL
Петров П.П. NULL NULL
Сидоров С.С. NULL NULL
Андреев А.А. NULL NULL


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


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
Иванов И.И. Директор Администрация
Петров П.П. Программист ИТ
Сидоров С.С. Бухгалтер Бухгалтерия
Андреев А.А. Старший программист ИТ


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

Так же стоит отметить, что таблица может ссылаться сама на себя, т.е. можно создать рекурсивную ссылку. Для примера добавим в нашу таблицу с сотрудниками еще одно поле 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)

)

 



php"; ?>