Каждая строка таблицы должна иметь уникальное значение первичного ключа, иначе база данных потеряет свою целостность. В ранних коммерческих СУБД первичные ключи отсутствовали, но сейчас они стали повсеместно распространенными. Первичные, ключи создаются с помощью инструкции CREATE TABLE. СУБД автоматически проверяет уникальность первичного ключа для каждой инструкции INSERT или UPDATE. Попытка добавить строку с уже существующим значением первичного ключа или обновить строку таким образом, что ее первичный ключ потеряет свою уникальность, завершится выдачей сообщения об ошибке.
Существует четыре типа изменений базы данных, которые могут нарушить ссылочную целостность отношений предок/потомок. Рассмотрим каждую из этих четырех ситуаций на примере таблиц BRANCH и STAFF.
• Добавление новой строки-потомка. Когда происходит добавление новой строки в таблицу – потомок (STAFF), значение ее внешнего ключа (BNO) должно быть равно одному из значений первичного ключа (BNO) в таблице-предке (BRANCH). Если значение внешнего ключа не равно ни одному из значений первичного ключа, то добавление такой строки разрушит базу данных, поскольку появится потомок без предка (“сирота”). Добавление строки в таблицу – предок не вызовет проблем; она просто станет предком без потомков.
• Обновление внешнего ключа в строке-потомке. Это та же проблема, что и в, предыдущей ситуации, но выраженная в иной форме. Если внешний ключ обновляется инструкцией UPDATE, то его новое значение должно быть равно одному из значений первичного ключа в таблице-предке. В противном случае обновленная строка окажется сиротой.
• Удаление строки-предка. Если из таблицы – предка (BRANCH) будет удалена строка, у которой есть хотя бы один потомок, то строки – потомки станут сиротами. Значения внешних ключей в этих строках больше не будут равны ни одному из значений первичного ключа таблицы – предка. Удаление строки из таблицы – потомка не вызовет проблем. В этом случае предок этой строки после удаления будет иметь на одного потомка меньше.
• Обновление первичного ключа в строке-предке. Это иная форма проблемы, рассмотренной в предыдущем пункте. Если происходит изменение первичного ключа некоторой строки в таблице – предке, все существующие потомки этой строки становятся сиротами, поскольку их внешние ключи больше не равны ни одному первичному ключу.
Средства поддержки ссылочной целостности стандарта ANSI/ISO, позволяют обрабатывать каждую из четырех описанных ситуаций. Первая проблема (добавление строки в таблицу – потомок) решается путем проверки значений в столбцах внешнего ключа перед выполнением инструкции INSERT. Если они не равны ни одному из значений первичного ключа, то выполнение инструкции INSERT влечет появление сообщения об ошибке.
Вторая проблема (обновление таблицы – потомка) решается аналогично: путем проверки нового значения внешнего ключа. Если нет ни одного равного ему значения первичного ключа, инструкция UPDATE отбрасывается с выдачей сообщения об ошибке.
Третья проблема (удаление строки-предка) является более сложной. Предположим, например, что вы закрыли отделение в Гродно и хотите удалить соответствующую строку из таблицы BRANCH. Как в этом случае поступить со строками – потомками таблицы STAFF? Очевидно, что в зависимости от ситуации можно:
• не удалять из базы данных отделение до тех пор, пока служащие не будут переведены в другое отделение;
• автоматически удалить всех соответствующих служащих из таблицы STAFF;
• в столбце STAFF.Bno установить значение NULL для всех соответствующих служащих, показывая тем самым, что идентификатор их офиса неизвестен;
• в столбце STAFF.Bno для соответствующих служащих установить по умолчанию некоторое значение, например идентификатор главного отделения в Минске, указывая тем самым, что служащие автоматически переводятся в это отделение.
Аналогичные сложности существуют и в четвертой ситуации (обновление первичного ключа в таблице – предке). Допустим, по каким-либо причинам требуется изменить идентификатор отделения в Витебске с 1 на 2. Подобно предыдущему примеру, возникает вопрос о том, как поступить со строками – потомками в таблице STAFF, представляющими служащих Витебского офиса. И снова проблему можно решить четырьмя способами:
• не изменять идентификатор отделения до тех пор, пока служащие не будут переведены в другое отделение; в таком случае в таблицу BRANCH следует вначале добавить строку с новым идентификатором отделения в Витебске, затем обновить таблицу STAFF и, наконец, удалить строку со старым идентификатором Витебского отделения;
• автоматически обновить идентификатор отделения этих служащих в таблице STAFF для того, чтобы их строки были по-прежнему связаны с Витебской строкой в таблице BRANCH через ее новый идентификатор;
• в столбце STAFF.Bno установить для соответствующих служащих значение NULL, показывая тем самым, что идентификатор их офиса неизвестен;
• в столбце STAFF.Bno установить по умолчанию для всех соответствующих служащих некоторое значение, например идентификатор главного офиса в Минске.
В данном конкретном примере некоторые способы решения могут показаться более логичными, чем другие, но относительно легко можно придумать примеры, в которых любая из четырех возможностей окажется наиболее “правильным” вариантом.
Эти правила были преобразованы в правила удаления и обновления, охватывающие как удаление строк – предков, так и обновление первичных ключей.