Что это - первичный ключ в базе данных? Понятие ключа в БД, первичные и внешние ключи Какие данные могут быть ключом бд.

16.09.2023 Android

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

СУБД – это программные средства для создания, наполнения, обновления и удаления БД.

Единицей хранящейся в БД информации является таблица. Каждая таблица представляет собой совокупность строк и столбцов, где строки соответствуют экземпляру объекта, конкретному событию или явлению, а столбцы – атрибутам (признакам, характеристикам, параметрам) объекта, события или явления. Каждая строка содержит сведения о конкретном событии.

В терминах БД столбцы таблицы называются полями, а её строки – записями.

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

Связанные отношениями таблицы взаимодействуют по принципу главная-подчиненная. Одна и та же таблица может быть главной к одной таблице БД и дочерней к другой.

Объект – это нечто существующее и различимое, обладающее набором свойств. Отличие одного объекта от другого объекта определяется конкретными значениями свойств.

Сущность – отражение объекта в памяти человека или компьютера.

Атрибут – конкретное значение любого из свойств сущности.

Поле – это один элемент записи, в котором хранится конкретное значение атрибута.

Поле связи это поле, по которому две таблицы связаны.

Первичные и вторичные ключи

В каждой таблице БД может существовать первичный ключ – это поле или табор полей, однозначно идентифицирующий запись.

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

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

Если в таблице нет полей, значения в которых уникально, для создания первичного ключа в неё обычно вводят дополнительное числовое поле, значениями которого СУБД может распоряжаться по своему усмотрению.

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

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

Реляционные отношения между таблицами

Один-к-одному. Отношение один-к-одному имеет место, когда одной записи в родительской таблице соответствует одна запись в дочерней таблице.

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

Подобно связи один-ко-многим, связь один-к-одному может быть жесткой и нежесткой.

InterBase могут использовать следующие виды ограничений:
  • PRIMARY KEY - первичный ключ таблицы.
  • UNIQUE - уникальный ключ таблицы.
  • FOREIGN KEY - внешний ключ , обеспечивает ссылку на другую таблицу и гарантирует ссылочную целостность между родительской и дочерней таблицами .

Примечание о терминологии

Если вы похожи на автора данного курса в том, что любите искать ответы на интересующий вас вопрос комплексно, в разных трудах разных авторов, то вы не могли не заметить некоторую путаницу в определениях главная (master) -> подчиненная (detail) таблицы. Напомним, что главную таблицу часто называют родительской, а подчиненную - дочерней.

Связано это, вероятно, с тем, как интерпретируются эти определения в локальных и SQL -серверных СУБД .

В локальных СУБД главной называется та таблица , которая содержит основные данные, а подчиненной - дополнительные. Возьмем, к примеру, три связанные таблицы. Первая содержит данные о продажах, вторая - о товарах и третья - о покупателях:


Рис. 18.1.

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

Но в SQL -серверах баз данных имеется другое определение связей: когда одно поле в таблице ссылается на поле другой таблицы, оно называется внешним ключом . А поле , на которое оно ссылается, называется родительским или первичным ключом . Таблицу, которая имеет внешний ключ (ссылку на запись другой таблицы) нередко называют дочерней, а таблицу с родительским ключом - родительской. Еще в определении связей говорят, что родитель может иметь только одну уникальную запись , на которую могут ссылаться несколько записей дочерней таблицы .

Так что в приведенном выше примере таблица продаж имеет два внешних ключа: идентификатор товара, и идентификатор покупателя. А обе таблицы в правой части рисунка имеют родительский ключ " Идентификатор ". Поскольку один покупатель или товар могут неоднократно встречаться в таблице продаж, то получается, что обе таблицы в правой части рисунка - родители, а таблица слева - дочерняя. Поскольку сейчас мы изучаем InterBase - SQL сервер БД , этими определениями мы и будем руководствоваться в последующих лекциях. Чтобы далее не ломать голову над этой путаницей, сразу договоримся: дочерняя таблица имеет внешний ключ (FOREIGN KEY ) на другую таблицу.

PRIMARY KEY

PRIMARY KEY - первичный ключ , является одним из основных видов ограничений в базе данных. Первичный ключ предназначен для однозначной идентификации записи в таблице, и должен быть уникальным. Первичные ключи PRIMARY KEY находятся в таблицах, которые принято называть родительскими (Parent ). Не стоит путать первичный ключ с первичными индексами локальных баз данных, первичный ключ является не индексом, а именно ограничением. При создании первичного ключа InterBase автоматически создает для него уникальный индекс . Однако если мы создадим уникальный индекс , это не приведет к созданию ограничения первичного ключа . Таблица может иметь только один первичный ключ PRIMARY KEY .

Предположим, имеется таблица со списком сотрудников. Поле "Фамилия" может содержать одинаковые значения (однофамильцы), поэтому его нельзя использовать в качестве первичного ключа. Редко, но встречаются однофамильцы, которые вдобавок имеют и одинаковые имена. Еще реже, но встречаются полные тезки, поэтому даже все три поля "Фамилия" + "Имя" + "Отчество" не могут гарантировать уникальности записи, и не могут быть первичным ключом. В данном случае выход , как и прежде, в том, чтобы добавить поле - идентификатор , которое содержит порядковый номер данного лица. Такие поля обычно делают автоинкрементными (об организации автоинкрементных полей поговорим на следующих лекциях). Итак,

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

Если в первичный ключ входит единственный столбец (как чаще всего и бывает), спецификатор PRIMARY KEY ставится при определении столбца :

CREATE TABLE Prim_1(Stolbec1 INT NOT NULL PRIMARY KEY, Stolbec2 VARCHAR(50))

Если первичный ключ строится по нескольким столбцам, то спецификатор ставится после определения всех полей:

CREATE TABLE Prim_2(Stolbec1 INT NOT NULL, Stolbec2 VARCHAR(50) NOT NULL, PRIMARY KEY (Stolbec1, Stolbec2))

Как видно из примеров, первичный ключ обязательно должен иметь ограничение столбца (столбцов) NOT NULL .

UNIQUE

UNIQUE - уникальный ключ . Спецификатор UNIQUE указывает, что все значения данного поля должны быть уникальными, в связи с этим такие поля также не могут содержать значения NULL . Можно сказать, что уникальный ключ UNIQUE является альтернативным вариантом первичного ключа, однако имеются различия. Главное различие в том, что первичный ключ должен быть только один, тогда как уникальных ключей может быть несколько. Кроме того, ограничение UNIQUE не может быть построено по тому же набору столбцов, который был использован для ограничения PRIMARY KEY или другого UNIQUE . Уникальные ключи, как и первичные, находятся в таблицах, которые являются родительскими по отношению к другим таблицам.

Столбец, объявленный с ограничением UNIQUE , как и первичный ключ , может применяться для обеспечения ссылочной целостности между родительской и дочерней таблицами . При этом внешний ключ дочерней таблицы будет ссылаться на это поле (поля). Как и в случае первичного ключа, при создании уникального ключа, для него автоматически будет создан уникальный индекс . Но не наоборот. Пример создания таблицы с одним первичным и двумя уникальными ключами:

CREATE TABLE Prim_3(Stolbec1 INT NOT NULL PRIMARY KEY, Stolbec2 VARCHAR(50) NOT NULL UNIQUE, Stolbec3 FLOAT NOT NULL UNIQUE)

FOREIGN KEY

FOREIGN KEY - внешний ключ . Это очень мощное средство для обеспечения ссылочной целостности между таблицами, которое позволяет не только следить за наличиями правильных ссылок, но и автоматически управлять ими. Внешние ключи содержатся в таблицах, которые являются дочерними (Child ) по отношению к другим таблицам. Ссылочная целостность обеспечивается именно внешним ключом, который ссылается на первичный или

ПРИМЕНЯЕТСЯ К: SQL Server (начиная с 2016)База данных SQL AzureХранилище данных SQL AzureParallel Data Warehouse

Первичные и внешние ключи представляют собой два типа ограничений, которые могут использоваться для обеспечения целостности данных в таблицах SQL Server. Это важные объекты базы данных.

Эта тема описана в следующих разделах.

Ограничения первичного ключа

Ограничения внешнего ключа

Связанные задачи

Обычно в таблице есть столбец или сочетание столбцов, содержащих значения, уникально определяющие каждую строку таблицы. Этот столбец, или столбцы, называются первичным ключом (PK) таблицы и обеспечивает целостность сущности таблицы. Ограничения первичного ключа часто определяются в столбце идентификаторов, поскольку гарантируют уникальность данных.

При задании ограничения первичного ключа для таблицы компонента Компонент Database Engine гарантирует уникальность данных путем автоматического создания уникального индекса для первичных ключевых столбцов. Этот индекс также обеспечивает быстрый доступ к данным при использовании первичного ключа в запросах. Если ограничение первичного ключа задано более чем для одного столбца, то значения могут дублироваться в пределах одного столбца, но каждое сочетание значений всех столбцов в определении ограничения первичного ключа должно быть уникальным.

Как показано на следующем рисунке, столбцы ProductID и VendorID в таблице Purchasing.ProductVendor формируют составное ограничение первичного ключа для данной таблицы. При этом гарантируется, что каждая строка в таблице ProductVendor имеет уникальное сочетание значений ProductID и VendorID . Это предотвращает вставку повторяющихся строк.

    В таблице возможно наличие только одного ограничения по первичному ключу.

    Первичный ключ не может включать больше 16 столбцов, а общая длина ключа не может превышать 900 байт.

    Индекс, формируемый ограничением первичного ключа, не может повлечь за собой выход количества индексов в таблице за пределы в 999 некластеризованных индексов и 1 кластеризованный.

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

    Все столбцы с ограничением первичного ключа должны быть определены как не допускающие значения NULL. Если допустимость значения NULL не указана, то все столбцы c ограничением первичного ключа устанавливаются как не допускающие значения NULL.

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

Внешний ключ (FK) - это столбец или сочетание столбцов, которое применяется для принудительного установления связи между данными в двух таблицах с целью контроля данных, которые могут храниться в таблице внешнего ключа. Если один или несколько столбцов, в которых находится первичный ключ для одной таблицы, упоминается в одном или нескольких столбцах другой таблицы, то в ссылке внешнего ключа создается связь между двумя таблицами. Этот столбец становится внешним ключом во второй таблице.

Например, таблица Sales.SalesOrderHeader связана с таблицей Sales.SalesPerson с помощью внешнего ключа, так как существует логическая связь между заказами на продажу и менеджерами по продажам. Столбец SalesPersonID в таблице Sales.SalesOrderHeader соответствует первичному ключевому столбцу в таблице SalesPerson . Столбец SalesPersonID в таблице Sales.SalesOrderHeader является внешним ключом для таблицы SalesPerson . С помощью установления данной связи по внешнему ключу значение для SalesPersonID не может быть вставлено в таблицу SalesOrderHeader , если оно в настоящий момент не содержится в таблице SalesPerson .

Максимальное количество таблиц и столбцов, на которые может ссылаться таблица в качестве внешних ключей (исходящих ссылок), равно 253. SQL Server 2016 увеличивает ограничение на количество других таблиц и столбцов, которые могут ссылаться на столбцы в одной таблице (входящие ссылки), с 253 до 10 000. (Требуется уровень совместимости не менее 130.) Увеличение имеет следующие ограничения:

    Превышение 253 ссылок на внешние ключи поддерживается только для операций DML DELETE. Операции UPDATE и MERGE не поддерживаются.

    Превышение 253 ссылок на внешние ключи в настоящее время недоступно для индексов columnstore, оптимизированных для памяти таблиц, базы данных Stretch или секционированных таблиц внешнего ключа.

Индексы в ограничениях внешнего ключа

В отличие от ограничений первичного ключа, при создании ограничения внешнего ключа соответствующий индекс автоматически не создается. Тем не менее, часто возникает необходимость создания индекса для внешнего ключа вручную по следующим причинам:

    Столбцы внешнего ключа часто используются в критериях соединения при совместном применении в запросах данных из связанных таблиц. Это реализуется путем сопоставления столбца или столбцов в ограничении внешнего ключа в одной таблице с одним или несколькими столбцами первичного или уникального ключа в другой таблице. Индекс позволяет компоненту Компонент Database Engine быстро находить связанные данные в таблице внешних ключей. Впрочем, создание индекса не является обязательным. Данные из двух связанных таблиц можно комбинировать, даже если между таблицами не определены ограничения первичного ключа или внешнего ключа, но связь по внешнему ключу между двумя таблицами показывает, что эти две таблицы оптимизированы для совместного применения в запросе, где ключи используются в качестве критериев.

    С помощью ограничений внешнего ключа в связанных таблицах проверяются изменения ограничений первичного ключа.

Ссылочная целостность

Главная задача ограничения внешнего ключа состоит в управлении данными, которые могут быть сохранены в таблице внешнего ключа, но это ограничение контролирует также изменение данных в таблице первичного ключа. Например, при удалении строки для менеджера по продажам из таблицы Sales.SalesPerson , идентификатор которого используется в заказах на продажу в таблице Sales.SalesOrderHeader , ссылочная целостность двух таблиц будет нарушена. Заказы на продажу удаленного менеджера в таблице SalesOrderHeader станут недействительными без связи с данными в таблице SalesPerson .

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

Каскадная ссылочная целостность

С помощью каскадных ограничений ссылочной целостности можно определять действия, которые компонент Компонент Database Engine будет предпринимать, когда пользователь попытается удалить или обновить ключ, на который указывают еще существующие внешние ключи. Могут быть определены следующие каскадные действия.

NO ACTION
Компонент Компонент Database Engine формирует ошибку, после чего выполняется откат операции удаления или обновления строки в родительской таблице.

CASCADE
Соответствующие строки обновляются или удаляются из ссылающейся таблицы, если данная строка обновляется или удаляется из родительской таблицы. Значение CASCADE не может быть указано, если столбец типа timestamp является частью внешнего или ссылочного ключа. Действие ON DELETE CASCADE не может быть указано в таблице, для которой определен триггер INSTEAD OF DELETE. Предложение ON UPDATE CASCADE не может быть задано применительно к таблицам, для которых определены триггеры INSTEAD OF UPDATE.

SET NULL
Всем значениям, составляющим внешний ключ, присваивается значение NULL, когда обновляется или удаляется соответствующая строка в родительской таблице. Для выполнения этого ограничения внешние ключевые столбцы должны допускать значения NULL. Не может быть задано применительно к таблицам, для которых определены триггеры INSTEAD OF UPDATE.

SET DEFAULT
Все значения, составляющие внешний ключ, при удалении или обновлении соответствующей строки родительской таблицы устанавливаются в значение по умолчанию. Для выполнения этого ограничения все внешние ключевые столбцы должны иметь определения по умолчанию. Если столбец допускает значения NULL и значение по умолчанию явно не определено, значением столбца по умолчанию становится NULL. Не может быть задано применительно к таблицам, для которых определены триггеры INSTEAD OF UPDATE.

Ключевые слова CASCADE, SET NULL, SET DEFAULT и NO ACTION можно сочетать в таблицах, имеющих взаимные ссылочные связи. Если компонент Компонент Database Engine обнаруживает ключевое слово NO ACTION, оно остановит и произведет откат связанных операций CASCADE, SET NULL и SET DEFAULT. Если инструкция DELETE содержит сочетание ключевых слов CASCADE, SET NULL, SET DEFAULT и NO ACTION, то все операции CASCADE, SET NULL и SET DEFAULT выполняются перед поиском компонентом Компонент Database Engine операции NO ACTION.

Триггеры и каскадные ссылочные действия

Каскадные ссылочные действия запускают триггеры AFTER UPDATE или AFTER DELETE следующим образом:

    Все каскадные ссылочные действия, прямо вызванные исходными инструкциями DELETE или UPDATE, выполняются первыми.

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

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

    Триггер AFTER таблицы, являвшейся непосредственной целью действий DELETE или UPDATE, запускается вне зависимости от того, были ли изменены хоть какие-нибудь строки. В этом случае ни на какие другие таблицы каскадирование не влияет.

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

    Выполнение операций CREATE, ALTER, DELETE или других операций языка DDL внутри триггеров может привести к запуску триггеров DDL. Это может привести к дальнейшим операциям DELETE или UPDATE, которые начнут дополнительные последовательности каскадных действий и запустят свои триггеры.

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

    У таблицы, для которой определен триггер INSTEAD OF, может также быть предложение REFERENCES, указывающее конкретное каскадное действие. Однако триггер AFTER целевой таблицы каскадного действия может выполнить инструкцию INSERT, UPDATE или DELETE для другой таблицы или представления, которое запустит триггер INSTEAD OF для этого объекта.

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

Вот так вот незаметно мы подошли к очень важной теме – первичных и внешних ключей. Если первые используются почти всеми, то вторые почему-то игнорируются. А зря. Внешние ключи – это не проблема, это реальная помощь в целостности данных.

1.2.5. Первичный ключ

Мы уже достаточно много говорили про ключевые поля, но ни разу их не использовали. Самое интересное, что все работало. Это преимущество, а может недостаток базы данных Microsoft SQL Server и MS Access. В таблицах Paradox такой трюк не пройдет и без наличия ключевого поля таблица будет доступна только для чтения.

В какой-то степени ключи являются ограничениями, и их можно было рассматривать вместе с оператором CHECK, потому что объявление происходит схожим образом и даже используется оператор CONSTRAINT. Давайте посмотрим на этот процесс на примере. Для этого создадим таблицу из двух полей "guid" и "vcName". При этом поле "guid" устанавливается как первичный ключ:

CREATE TABLE Globally_Unique_Data (guid uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (Guid))

Самое вкусное здесь это строка CONSTRAINT. Как мы знаем, после этого ключевого слова идет название ограничения, и объявления ключа не является исключением. Для именования первичного ключа, я рекомендую использовать именование типа PK_имя, где имя – это имя поля, которое должно стать главным ключом. Сокращение PK происходит от Primary Key (первичный ключ).

После этого, вместо ключевого слова CHECK, которое мы использовали в ограничениях, стоит оператор PRIMARY KEY, Именно это указывает на то, что нам необходима не проверка, а первичный ключ. В скобках указывается одно, или несколько полей, которые будут составлять ключ.

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

В данном примере, в качестве первичного ключа выступает поле типа uniqueidentifier (GUID). Значение по умолчанию для этого поля – результат выполнения серверной процедуры NEWID.

Внимание

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

Для простоты примеров, в качестве ключа желательно использовать численный тип и если позволяет база данных, то будет лучше, если он будет типа "autoincrement" (автоматически увеличивающееся/уменьшающееся число). В MS SQL Server таким полем является IDENTITY, а в MS Access это поле типа «счетчик».

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

CREATE TABLE Товары (id int IDENTITY(1, 1), товар varchar(50), Цена money, Количество numeric(10, 2), CONSTRAINT PK_id PRIMARY KEY (id))

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

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

CREATE TABLE Товары1 (id int IDENTITY(1, 1), Товар varchar(50), Цена money, Количество numeric(10, 2), CONSTRAINT PK_id PRIMARY KEY (id, [Название товара]))

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

Единственный недостаток первичного ключа из нескольких колонок – проблемы создания связей. Тут приходиться выкручиваться различными методами, но проблема все же решаема. Достаточно только ввести поле типа uniqueidentifier и производить связь по нему. Да, в этом случае у нас получаются уникальными первичный ключ и поле типа uniqueidentifier, но эта избыточность в результате не будет больше, чем та же таблица, где первичный ключ uniqueidentifier, а на поля, которые должны быть уникальными установлено ограничение уникальности. Что выбрать? Зависит от конкретной задачи и от того, с чем вам удобнее работать.

1.2.6. Внешний ключ

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

  • Names – содержит имена людей и состоит из полей идентификатора (ключевое поле), имя.
  • Phones – таблица телефонов, которая состоит из идентификатора (ключевое поле), внешний ключ для связи с таблицей names и строковое поле для хранения номера телефона.

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

Для примера возьмем таблицу из трех человек. В таблице 1.3 показано содержимое таблицы "Names". Здесь всего три строки и у каждой свой уникальный главный ключ. Для уникальности, когда будем создавать таблицу, сделаем ключ автоматически увеличиваемым полем.

Таблица 1.3 Содержимое таблицы Names

Таблица 1.4. Содержимое таблицы Phones

В таблице 1.4 находится пять номеров телефонов. В поле главный ключ также уникальный главный ключ, которой также можно сделать автоматически увеличиваемым. Вторичный ключ – это связь с главным ключом таблицы Names. Как работает эта связь? У Петрова в таблице Names в качестве главного ключа стоит число 1. В таблице Phones во вторичном ключе ищем число 1 и получаем номера телефонов Петрова. То же самое и с остальными записями. Визуально связь можно увидеть на рисунке 1.5.

Такое хранение данных очень удобно. Если бы не было возможности создавать связанные таблицы, то в таблице Names пришлось бы забивать все номера телефонов в одно поле. Это неудобно с точки зрения использования, поддержки и поиска данных.

Можно создать в таблице несколько полей Names, но возникает вопрос – сколько. У одного человека может быть только 1 телефон, а у меня, например, их 3, не считая рабочих. Большое количество полей приводит к избыточности данных.

Можно для каждого телефона в таблице Names заводить отдельную строку с фамилией, но это легко только для такого простого примера, когда нужно вводить только фамилию и легко можно внести несколько записей для Петрова с несколькими номерами телефонов. А если полей будет 10 или 20? Итак, создание двух таблиц связанных внешним ключом можно увидеть в листинге 1.6.

Листинг 1.6. Создание таблиц связанных внешним ключом

CREATE TABLE Names (idName int IDENTITY(1,1), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName),) CREATE TABLE Phones (idPhone int IDENTITY(1,1), idName int, vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone), CONSTRAINT FK_idName FOREIGN KEY (idName) REFERENCES Names (idName))

Внимательно изучите содержимое листинга. Он достаточно интересен, потому что использует некоторые операторы, которые мы уже рассмотрели и дополнительный пример не помешает. Для обеих таблиц создается ключевое поле, которое стоит первым, имеет тип int и автоматически увеличивается, начиная с 1 с приращением в единицу. Ключевое поле делается главным ключом с помощью ограничение CONSTRAINT.

В описании таблицы Phones последняя строка содержит новое для нас объявление, а именно – объявление внешнего ключа с помощью оператора FOREIGN KEY. Как видите, это тоже ограничение и чуть позже вы увидите почему. В скобках указывается поле таблицы, которое должно быть связано с другой таблицей. После этого идет ключевое слово REFERENCES (ссылка), имя таблицы, с которой должна быть связь (Names) и в скобках имя поля ("idName"). Таким образом, мы навели связь, которая отображена на рисунке 1.4.

Внимание!

Внешний ключ может ссылаться только на первичный ключ другой таблицы или на ограничение уникальности. Это значит, что после ключевого слова REFERENCES должно быть имя таблицы и в скобках можно указывать только первичный ключ или поле с ограничением UNIQUE. Другие поля указывать нельзя.

Теперь, если можно наполнять таблицы данными. Следующие три команды добавляют три фамилии, которые мы видели в таблице 1.3:

INSERT INTO Names(vcName) VALUES("Петров") INSERT INTO Names(vcName) VALUES("Иванов") INSERT INTO Names(vcName) VALUES("Сидоров")

Если вы уже работали с SQL то сможете добавить записи и для таблицы телефонов. Я опущу эти команды, а вы можете увидеть их в файле foreign_keys.sql директории Chapter1 на компакт диске.

Наша задача сейчас увидеть, в чем заключаются ограничительные действия внешнего ключа, давайте разберемся. Мы указали явную связь между двумя полями в разных таблицах. Если попытаться добавить в таблицу телефонов запись с идентификатором в поле "idName", не существующим в одноименном поле (имя можно было сделать и другим) таблице с фамилиями, то произойдет ошибка. Это нарушит связь между двумя таблицами, а ограничение внешнего ключа не позволит существовать записям без связи.

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

Во время создания внешнего ключа, можно указать ON DELETE CASCADE или ON UPDATE CASCADE. В этом случае, если удалить запись Петрова из таблице Names или изменить идентификатор, то все записи в таблице Phones, связанные со строкой Петрова будут автоматически обновлены. Никогда. Нет, нужно написать большими буквами: НИКОГДА не делайте этого. Все должно удаляться или изменяться вручную. Если пользователь случайно удалит запись из таблицы Names, то удаляться и соответствующие телефоны. Смысл тогда создавать внешний ключ, если половина его ограничительных возможностей исчезает! Все необходимо делать только вручную, а идентификаторы изменять не рекомендуется вообще никогда.

Удаление самих таблиц также должно начинаться с подчиненной таблицы, то есть с Phones, и только потом можно удалить главную таблицу Names.

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

SELECT vcName, vcPhone FROM Names, Phones WHERE Names.idName=Phones.idName

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

Таблица может содержать до 253 внешних ключей, что вполне достаточно даже для построения самых сложных баз данных. Лично мне приходилось работать с базами данных, где количество внешних ключей не превышало 7 на одну таблицу. Если больше, то скорей всего база данных спроектирована неверно, хотя бывают и исключения.

Сама таблица также может иметь максимум 253 внешних ключей. Внешние ключи в таблице встречаются реже, в основном не более 3. Чаще всего в таблице может быть много ссылок на другие таблицы.

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

Таблица 1.5. Таблица с внутренней связью

В результате мы получаем, что у генерального директора внешний ключ нулевой, т.е. эта должность стоит во главе всех остальных. У коммерческого директора и директора по общим вопросам внешний ключ указывает на строку генерального директора. Это значит, что эти две должности подчиняются непосредственно генеральному директору. И так далее.

Посмотрим, как можно создать все это в виде SQL запроса:

CREATE TABLE Positions (idPosition int IDENTITY(1,1), idParentPosition int, vcName varchar(30), CONSTRAINT PK_idPosition PRIMARY KEY (idPosition), CONSTRAINT FK_idParentPosition FOREIGN KEY (idParentPosition) REFERENCES Positions (idPosition))

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

Отношение один к одному

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

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

CREATE TABLE Names (idName uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName)) CREATE TABLE Phones (idPhone uniqueidentifier DEFAULT NEWID(), vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone), CONSTRAINT FK_idPhone FOREIGN KEY (idPhone) REFERENCES Names (idName))

Внешний ключ нужен только у одной из таблиц. Так как связь идет один к одному, то не имеет значения, в какой таблице создать его.

Многие ко многим

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

Для начала нужно понять, когда может использоваться связь многие ко многим? Допустим, что у вас есть две таблицы: список жителей дома и список номеров телефона. В одной квартире может быть более одного номера, а значит, одной фамилии может принадлежать два телефона. Получается, связь один ко многим. С другой стороны, в одной квартире может быть две семьи (коммунальная квартира или просто квартиросъемщик, который пользуется телефоном владельца), а значит, связь между телефоном и жителем тоже один ко многим. И самый сложный вариант – в коммунальной квартире находиться два телефона. В этом случае обоими номерами пользуются несколько жителей квартире. Вот и получается, что "много" семей может пользоваться "многими" телефонами (связь многие ко многим).

Как реализовать связь многие ко многим? На первый взгляд, в реляционной модели это невозможно. Лет 10 назад я долго искал разные варианты и в результате просто создавал одну таблицу, которая была переполнена избыточностью данных. Но однажды, мне досталась одна задача, благодаря которой уже из условия на поверхность вышло отличное решение – нужно создать две таблицы жителей квартир и телефонов и реализовать в них только первичный ключ. Внешние ключи в этой таблице не нужны. А вот связь между таблицами должна быть через третью, связующую таблицу. На первый взгляд это сложно и не понятно, но один раз разобравшись с этим методом, вы увидите всю мощь этого решения.

В таблицах 1.6 и 1.7 показаны примеры таблиц фамилий и телефонов соответственно. А в таблице 1.8 показана связующая таблица.

Таблица 1.6. Таблица фамилий

Таблица 1.7. Таблица телефонов

Таблица 1.8. Таблица телефонов

Давайте теперь посмотрим, какая будет логика поиска данных при связи многие ко многим. Допустим, что нам нужно найти все телефоны, которые принадлежат Иванову. У Иванова первичный ключ равен 1. Находим в связующей таблице все записи, у которых поле "Связь с именем" равно 1. Это будут записи 1 и 2. В этих записях в поле "Связь с телефоном" находятся идентификаторы 1 и 2 соответственно, а значит, Иванову принадлежат номера из таблицы телефонов, которые расположены в строках 1 и 2.

Теперь решим обратную задачу – определим, кто имеет доступ к номеру телефона 567575677. Этот номер в таблице телефонов имеет ключ 3. Ищем все записи в связующей таблице, где в поле "Связь с телефоном" равно 3. Это записи с номерами 4 и 5, которые в поле "Связь с именем" содержат значения 2 и 3 соответственно. Если теперь посмотреть на таблицу фамилий, то вы увидите под номерами 2 и 3 Петрова и Сидорова. Значит, именно эти два жителя пользуются телефоном с номером 567575677.

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

CREATE TABLE Names (idName uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName)) CREATE TABLE Phones (idPhone uniqueidentifier DEFAULT NEWID(), vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone)) CREATE TABLE LinkTable (idLinkTable uniqueidentifier DEFAULT NEWID(), idName uniqueidentifier, idPhone uniqueidentifier, CONSTRAINT PK_idLinkTable PRIMARY KEY (idLinkTable), CONSTRAINT FK_idPhone FOREIGN KEY (idPhone) REFERENCES Phones (idPhone), CONSTRAINT FK_idName FOREIGN KEY (idName) REFERENCES Names (idName))

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

В качестве первичного ключа я выбрал GUID поле, потому что для решения именно этой задачи он более удобен. Дело в том, что нам нужно вставлять записи в две таблицы и в обоих случаях нужно указывать один и тот же ключ. Значение GUID можно сгенерировать, а потом можно использовать при вставке данных в обе таблицы.

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

На данном этапе мы ограничиваемся только созданием таблиц, а в разделе 2.8 мы вернемся к этой теме и научимся и научимся работать со связанными таблицами. Работа со связью один к одному и один ко многим отличается не сильно, потому что в этой схеме участвует только две таблицы. Связь многие ко многим немного сложнее из-за связующей таблицы, поэтому мы ее рассмотрим отдельно в разделе 2.27.

Ключи являются фундаментальными элементами реляционной базы данных, поскольку они устанавливают связь между парой таблиц и обеспечивают уникальную идентификацию каждой записи в таблице. Ключи имеют более важное значение, чем установление отношений; они также помогают с ссылочной целостностью, и они являются основным компонентом целостности на уровне таблиц. Таблицы хранят в них огромные куски данных, которые обычно распространяются на тысячи записей, все из которых несортированы и дезорганизованы. Получение определенных данных из этих многочисленных записей может быть затруднительным порой или иногда невозможным. Именно здесь появляются Ключи. Здесь мы рассмотрим два очень важных ключа схемы реляционной базы данных и разницу между ними: Первичный ключ и Внешний ключ.

Что такое первичный ключ?

Первичный ключ - это специальный ключ, который однозначно идентифицирует каждую запись в таблице. В реляционной базе данных очень важно иметь уникальный идентификатор в каждой строке таблицы, а первичный ключ - это просто то, что вам нужно, чтобы однозначно идентифицировать кортеж в таблице. Кортеж представляет собой набор атрибутов value в реляционной базе данных. Первичный ключ может ссылаться на столбец или набор столбцов в таблице реляционной базы данных, используемый для неявной идентификации всех записей в таблице. Первичный ключ должен быть уникальным для каждой записи, поскольку он действует как уникальный идентификатор и не должен содержать значений Null. Каждая база данных должна иметь один и только один первичный ключ.

Что такое внешний ключ?

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

Разница между основным ключом и внешним ключом

Основы первичного ключа и внешнего ключа

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

Отношение первичного ключа и внешнего ключа

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

Дублирующие значения первичного ключа и внешнего ключа

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

NULL первичного ключа и внешнего ключа

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

Временная таблица первичного ключа и внешнего ключа

Ограничение первичного ключа может быть определено неявно на временных таблицах и их переменных, тогда как ограничение внешнего ключа не может быть применено к локальным или глобальным временным таблицам.

Удаление основного ключа и внешнего ключа

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

Первичный ключ или внешний ключ: сравнительная таблица

Резюме основных ключей

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