Изучи сервер Windows SQL 2000 за 15 минут в неделю:
 

Таблицы - Часть 2 - Ссылочная целостность

от Mike Aubert

 

Добро пожаловать в статью 12 моей серии «Изучи администрирование SQL Server за 15 минут в неделю!». В предыдущей статье мы рассмотрели различные типы данных используемых в SQL Server и то, как добавлять таблицы в базу данных. На этой неделе мы продолжим наше обсуждение таблиц и рассмотрим, как реализовать ссылочную целостность (referential integrity). В статью вошли следующие темы:

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

ACID

Давайте вернемся к статье, посвященной созданию баз данных, в которой я приводил пример транзакции, переводящей деньги с одного банковского счета на другой. Этот пример иллюстрировал, что транзакция препятствует изменению счета А до тех пор, пока не будет завершено изменение счета В. Иначе говоря, в ходе выполнения транзакции, либо все изменения будут внесены в базу данных (commit) или произойдет откат (roll back) всех изменений. Давайте возьмем наше определение транзакции и расширим его.

Короткое определение транзакции звучит так: «последовательность операций, выполняемых как логическая единица работы». Будем считать, что «транзакция» - это целостная логическая единица работы, обладающая четырьмя основными свойствами. Эти свойства (атомарность, согласованность, изоляция и устойчивость) называются ACID-свойствами (от англ. Atomicity, Consistency, Isolation, and Durability):

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

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

Изоляция – Изменения, вносимые одновременно выполняемыми транзакциями должны быть изолированы от изменений, выполняемых любой из них и всеми другими одновременными транзакциями. Другими словами, транзакция либо «видит» данные в том состоянии, в котором они были перед тем, как другая одновременная транзакция изменит их, либо «увидит» данные уже после того, как вторая транзакция будет завершена. Но транзакция не может «видеть» данные в промежуточном состоянии. Это процесс известен как сериализация (упорядочение), потому что он может воспроизводить результаты транзакций, возвращая данные в стартовое состояние, и «вновь повторяя» серию транзакций.

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

Хорошая новость заключается в том, что с физической и технической точки зрения SQL Server способен полностью обеспечить реализацию ACID-свойств. Однако, логика, лежащая в основе того, что считать транзакцией и является или нет изменение правомочным – это всегда работа администратора баз данных и/или программиста.

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

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

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

Когда таблицы организованы в базу данных, большинство из них, если не все, имеют логическую связь друг с другом. Например, каждой строке таблицы Order (заказ) может соответствовать несколько строк таблицы Order Detail (детали заказа), каждой строке таблицы Salesperson (продавец) – несколько строк из Order и т.д. (если вы не очень понимаете, о чем идет речь, предлагаю вам остановиться и почитать статью о логической структуре баз данных)… что же нам необходимо сделать? Подтверждать достоверность! Приведу пример:

Представим себе, что у меня есть две таблицы, называемые Order и OrderDetail. Каждому заказу может соответствовать множество деталей заказа. Каждая деталь заказа соответствует одному и только одному заказу. Что же случится, если программа, добавив несколько строк в OrderDetail, «зависнет» перед тем, как добавить соответствующие строки в таблицу Order? Или что произойдет, если приложение удалит строку из таблицы Order без удаления соответствующих строк из таблицы OrderDetail? Короче говоря, мы получим лишние дочерние строки (в таблице с внешним ключом), не имеющие соответствующих им родительских строк (в таблице с первичным ключом).

В идеальном случае все приложения должны изменять таблицы должным образом. Но наш мир несовершенен и случаются ситуации (и будут случаться всегда), подобные описанным выше. Для небольшого проекта, в котором программист является также администратором БД, возможно уменьшить количество нарушений, старательно разрабатывая структуру приложений… но что же может случиться, если к базе данных одновременно подключается дюжина приложений, написанных разными программистами?! Простая ошибка может нарушить целостность базы данных и вы никогда не узнаете, откуда она появилась. <soapbox>Повторяйте за мной: «Это обязанность СУРБД (системы управления реляционной базой данных), а не конечного приложения, проверять ссылочную целостность!»</soapbox>

Для того чтобы активизировать ссылочную целостность между таблицами, необходимо установить ограничение FOREIGN KEY или «связь» в SQL Server. Для того чтобы продемонстрировать это я создал две таблицы – Parent и Child. Столбец ParentID в таблице Child является внешним ключом, используемым для ссылок на родительскую (для дочерней) таблицу.

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

До этого момента еще возможно существование таблицы Child без Parent.

Для того чтобы установить связь и создать ссылочную целостность:

1. Откройте в дизайнере таблиц (table designer) одну из таблиц, которая станет частью отношения (в нашем примете это таблица Parent) и щелкните в панели инструментов кнопку “Manage Relationships…” (управление связями). Появится следующее окно:

2. Щелкните New.

3. Установите таблицу Parent в качестве “Primary key table” (таблицы с первичным ключом) и Child в качестве “Foreign key table” (таблицы с внешним ключом).

4. Под полем Parent выберите ID в первом поле. Под Child выберите ParentID. Ваш экран должен выглядеть так:

Перед тем, как вы щелкните Close и сохраните таблицу, давайте рассмотрим опции в нижней части этого окна:

Check existing data on creation (Проверять существующие данные при создании) – применяет ограничение для данных, уже существующих  в базе данных, при добавлении связи в таблицу с внешним ключом.

Enforce relationship for replication (Форсировать связь через репликацию) – применяет ограничение при копировании таблицы с внешним ключом на другую базу данных (мы вернемся к репликации позднее).

Enforce relationship for INSERTs and UPDATEs (Форсировать связь для операторов INSERT и UPDATE) – применяет ограничение для данных, вводимых, удаляемых или обновляемых в таблице с внешним ключом. Также предотвращает удаление строки в таблице с первичным ключом, если соответствующая строка существует в таблице с внешним ключом.

Cascade Update Related Fields (Каскадное обновление связанных полей) – приказывает СУБД автоматически обновлять значения внешнего ключа этой связи, если обновляется значение первичного ключа. Например: Если эта опция выбрана и значение CustomerID меняется с 1 на 2, то любая таблица с внешним ключом (такая как Order), ссылающаяся на CustomerID, будет обновлена так, чтобы ссылаться на новые значения CustomerID.

Cascade Delete Related Fields (Каскадное удаление связанных полей) – приказывает СУБД автоматически удалять строки таблицы с внешним ключом, если соответствующие строки в таблице с первичным ключом были удалены. Например: если эта опция выбрана и заказ был удален из таблицы Order, все строки с внешними ключами (например, в таблице OrderDetails), ссылающиеся на данный заказ будут также удалены.

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

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

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

  • Столбец первичной (или родительской) таблицы должен быть первичным ключом или иметь установленное на нем ограничение UNIQUE;
  • Связанные столбцы в таблицах с первичным и внешним ключами должны иметь одинаковый тип данных и размер;
  • Обе таблицы должны находиться в одной базе данных.

Также помните, что такие факторы, как допуск пустых значений или наличие ограничения UNIQUE в столбце с внешним ключом (и еще раз: мы рассмотрим, как добавлять ограничение UNIQUE позднее) могут приводить к тому, что связи можно будет создать или, наоборот, нельзя. Например: если я разрешу столбцу ParentID в таблице Child принимать пустые значения, то появится возможность добавлять множество строк (допустим, что на столбце ParentID таблицы Child нет ограничения UNIQUE), содержащих <NULL>, или «ничего» в ячейках столбца ParentID. Этот случай показан на следующем рисунке:

Если в столбце ParentID будет что-либо отличное от <NULL>, то связь между таблицами с первичным и внешним ключами может быть установлена. То есть, если в строке, добавленной в приведенную выше таблицу, для ParentID установлено значение 5 (вместо <NULL>), то соответствующая строка с ID равным 5 должна существовать в таблице Parent.

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

1. Должна ли каждая строка таблицы с внешним ключом иметь соответствующую строку в таблице с первичным ключом?

  • Да – Удалите флажок “Allow Null” для столбца с внешним ключом;
  • Нет – Установите флажок “Allow Null” для столбца с внешним ключом.

2. Какой тип связи существует между таблицами?

  • Один к одному – Добавьте ограничение UNIQUE в столбец с внешним ключом;
  • Один ко многим – Не добавляйте ограничение UNIQUE в столбец с внешним ключом;
  • Многие ко многим – Создайте дополнительную таблицу и не добавляйте ограничение UNIQUE ни в один из столбцов с внешним ключом.

И последнее: поскольку Enterprise Manager позволяет нам делать практически все… он также позволяет устанавливать связи при помощи SQL операторов. Посмотрите определение операторов CREATE TABLE и ALTER TABLE в справочной системе SQL Server.

Итак, это все на этой неделе. На следующей мы продолжим наше обсуждение ограничений, если говорить конкретнее, мы рассмотрим создание ограничения CHECK. И как всегда…если у вас возникнут какие-либо технические вопросы, пожалуйста, присылайте их на доску объявлений сайта 2000Trainers.com SQL message board. Нетехнические вопросы, комментарии и обратная связь – по адресу моей электронной почты. Я надеюсь, что вы найдете эти статьи полезными и был бы рад узнать ваше мнение о них.

Mike Aubert, MCSE, MCDBA,  MCSD.

Hosted by uCoz