|
||||||
19. ПОДДЕРЖКА ЦЕЛОСТНОСТИ ВАШИХ ДАННЫХ Ранее в этой книге, мы указывали на определенные связи которые существуют между некоторыми полями типовых таблиц. Поле snum таблицы Заказчиков, например, соответствует полю snum в таблице Продавцов и таблице Порядков. Поле cnum таблицы Заказчиков также соответствует полю cnum таблицы Порядков. Мы назвали этот тип связи - справочной целостностью; и в ходе обсуждения, вы видели как ее можно использовать. ВНЕШНИЙ КЛЮЧ И РОДИТЕЛЬСКИЙ КЛЮЧКогда все значения в одном поле таблицы представлены в поле другой таблицы, мы говорим что первое поле ссылается на второе. Это указывает на прямую связь между значениями двух полей. Например, каждый из заказчиков в таблице Заказчиков имеет поле snum которое ука- зывает на продавца назначенного в таблице Продавцов. Для каждого порядка в таблице Порядков, имеется один и только этот про- давец и один и только этот заказчик. Это отображается с помощью полей snum и cnum в таблице Порядков.Когда одно поле в таблице ссылается на другое, оно называется - внешним ключом; а поле на которое оно ссылается, называется - родительским ключом. Так что поле snum таблицы Заказчиков - это внешний ключ, а поле snum на которое оно ссылается в таблице Продавцов - это родительский ключ. Аналогично, пол cnum и snum таблицы Порядков - это внешние ключи которые ссылаются к их родительским ключам с именами в таблице За- казчиков и таблице Продавцов. Имена внешнего ключа и родительского ключа не обязательно должны быть одинаковыми, это - только соглашение которому мы следуем чтобы делать соединение более понятным. МНОГО-СТОЛБЦОВЫЕ ВНЕШНИЕ КЛЮЧИВ действительности, внешний ключ не обязательно состоит только из одного пол. Подобно первичному ключу, внешний ключ может иметь любое число полей, которые все обрабатываются как единый модуль. Внешний ключ и родительский ключ на который он ссылается, конечно же, должны иметь одинаковый номер и тип пол, и находиться в одинаковом порядке. Внешние ключи состоящие из одного пол - те что мы использовали исключительно в наших типовых таблицах, наиболее общие. Чтобы сохранить простоту нашего обсуждения, мы будем часто говорить о внешнем ключе как об одиночном столбце. Это не случайно. Если это не от- метить, любой скажет о поле которое является внешним ключом, что оно также относится и к группе полей которая является внешним ключом.СМЫСЛ ВНЕШНЕГО И РОДИТЕЛЬСКОГО КЛЮЧЕЙКогда поле - является внешним ключом, оно определенным образом связано с таблицей на которую он ссылается. Вы, фактически, говорите - " каждое значение в этом поле ( внешнем ключе ) непосредственно привязано к зна- чению в другом поле ( родительском ключе )." Каждое значение ( каждая строка ) внешнего ключа должно недвусмысленно ссылаться к одному и только этому значению (строке) родительского ключа. Если это так, то фактически ваша система, как говорится, будет в состоянии справочной целостности. Вы можете увидеть это на примере. Внешний ключ snum в таблице Заказ- чиков имеет значение 1001 для строк Hoffman и Clemens. Предположим что мы имели две строки в таблице Продавцов со значением в поле snum = 1001. Как мы узнаем, к которому из двух продавцов были назначены заказчики Hoffman и Clemens ? Аналогично, если нет никаких таких строк в таблице Продавцов, мы получим Hoffman и Clemens назначенными к продавцу которого не существует!Понятно, что каждое значение во внешнем ключе должно быть представ- лено один, и только один раз, в родительском ключе. Фактически, данное значение внешнего ключа может ссылаться только к одному значению родительского ключа не предполагая обратной возможности: т.е. любое число внешних ключей может ссылать к единственному значению родительского ключа. Вы можете увидеть это в типовых таблицах наших примеров. И Hoffman и Clemens назначены к Peel, так что оба их значения внешнего ключа совпадают с одним и тем же родительским ключом, что очень хорошо. Значение внешнего ключа должно ссылаться только к одному значению родительского ключа, зато значение родительского ключа может ссылаться с помощью любого количества значений внешнего ключа. В качестве иллюстрации, значения внешнего ключа из таблицы Заказчиков, совпавшие с их родительским ключом в Продавцов таблице, показываются в Рисунке 19.1. Для удобства мы не учитывали пол не относящиеся к этому примеру. ОГРАНИЧЕНИЕ FOREIGN KEYSQL поддерживает справочную целостность с ограничением FOREIGN KEY. Хотя ограничение FOREIGN KEY - это нова особенность в SQL, оно еще не обеспечивает его универсальности. Кроме того, некоторые его реализации, более сложны чем другие. Эта функция должна ограничивать значения которые вы можете ввести в вашу базу данных чтобы заставить внешний ключ и родительский ключ соответствовать принципу справочной целостности. Одно из действий ограничения Внешнего Ключа - это отбрасывание значений для полей ограниченных как внешний ключ который еще не представлен в ро- дительском ключе. Это ограничение также воздействует на вашу способность изменять или удалять значения родительского ключа ( мы будем обсуждать это позже в этой главе ).КАК МОЖНО ПОЛЯ ПРЕДСТАВИТЬ В КАЧЕСТВЕ ВНЕШНИХ КЛЮЧЕЙВы используете ограничение FOREIGN KEY в команде CREATE TABLE ( или ALTER TABLE ), которая содержит поле которое вы хотите объявить внешним ключом. Вы даете им родительскому ключу на которое вы будете ссылаться внутри ограничения FOREIGN KEY. Помещение этого ограничения в команду - такое же что в для других ограничений обсужденных в предыдущей главе. Рисунок 19.1: Внешний Ключ таблицы Заказчиков с родительским ключомПодобно большинству ограничений, оно может быть ограничением таблицы или столбца, в форме таблицы позволяющей использовать многочисленные пол как один внешний ключ. ВНЕШНИЙ КЛЮЧ КАК ОГРАНИЧЕНИЕ ТАБЛИЦЫСинтаксис ограничения таблицы FOREIGN KEY:FOREIGN KEY <COLUMN list> REFERENCES <PKTABLE> [ <COLUMN list> ]Первый список столбцов - это список из одного или более столбцов таблицы, которые отделены запятыми и будут созданы или изменены этой командой. Pktable - это таблица содержащая родительский ключ. Она может быть таблицей, которая создается или изменяется текущей командой. Второй список столбцов - это список столбцов которые будут составлять родительский ключ. Списки двух столбцов должны быть совместимы, т.е.: * Они должны иметь одинаковое число столбцов. * В данной последовательности, первый, второй, третий, и т.д., столбцы списка столбцов внешнего ключа, должны иметь одинаковые типы данных и размеры, что и первый, второй, третий, и т.д., столбцы списка столбцов родительского ключа. Столбцы в списках обоих столбцов не должны иметь одинаковых имен, хотя мы и использовали такой способ в наших примерах чтобы делать связь более понятной. Создадим таблицу Заказчиков с полем snum определенным в качестве внешнего ключа ссылающегося на таблицу Продавцов: CREATE TABLE Customers ( cnum integer NOT NULL PRIMARY KEY cname char(10), city char(10), snum integer, FOREIGN KEY (snum) REFERENCES Salespeople ( snum );Имейте в виду, что при использовании ALTER TABLE вместо CREATE TABLE, для применения ограничения FOREIGN KEY, значения которые Вы указываете во внешнем ключе и родительском ключе, должны быть в состоянии справочной целостности. Иначе команда будет отклонена. Хотя ALTER TABLE очень полезна из-за ее удобства, вы должны будете в вашей системе, по возможности каждый раз, сначала формировать структурные принципы, типа справочной целостности. ВНЕШНИЙ КЛЮЧ КАК ОГРАНИЧЕНИЕ СТОЛБЦОВВариант ограничения столбца ограничением FOREIGN KEY - по другому называется - ссылочное ограничение (REFERENCES), так как он фактически не со- держит в себе слов FOREIGN KEY, а просто использует слово REFERENCES, и далее им родительского ключа, подобно этому:CREATE TABLE Customers ( cnum integer NOT NULL PRIMARY KEY, cname char(10), city char(10), snum integer REFERENCES Salespeople (snum));Вышеупомянутое определяет Customers.snum как внешний ключ у которого родительский ключ - это Salespeople.snum. Это эквивалентно такому ограничению таблицы: FOREIGN KEY (snum) REGERENCES Salespeople (snum) НЕ УКАЗЫВАТЬ СПИСОК СТОЛБЦОВ ПЕРВИЧНЫХ КЛЮЧЕЙMИспользуя ограничение FOREIGN KEY таблицы или столбца, вы можете не указывать список столбцов родительского ключа если родительский ключ имеет ограничение PRIMARY KEY. Естественно, в случае ключей со многими полями, порядок столбцов во внешних и первичных ключах должен совпадать, и, в любом случае, принцип совместимости между двум ключами все еще применим. Например, если мы поместили ограничение PRIMARY KEY в поле snum таблицы Продавцов, мы могли бы использовать его как внешний ключ в таблице Заказчиков (подобно предыдущему примеру) в этой команде:CREATE TABLE Customers ( cnum integer NOT NULL PRIMARY KEY, cname char(10), city char(10), snum integer REFERENCES Salespeople);Это средство встраивалось в язык, чтобы поощрять вас использовать первич- ные ключи в качестве родительских ключей. КАК СПРАВОЧНАЯ ЦЕЛОСТНОСТЬ ОГРАНИЧИВАЕТ ЗНАЧЕНИЯ РОДИТЕЛЬСКОГО КЛЮЧАПоддержание справочной целостности требует некоторых ограничений на значения, которые могут быть представлены в полях, объявленных как внешний ключ и родительский ключ. Родительский ключ должен быть структурен, чтобы гарантировать, что каждое значение внешнего ключа будет соответствовать одной указанной строке. Это означает, что он (ключ) должен быть уникальным и не содержать никаких пустых значений(NULL). Этого не достаточно для родительского ключа в случае выполнения такого требования как при объявлении внешнего ключа. SQL должен быть уверен что двойные значения или пустые значения (NULL) не были введены в родительский ключ. Следовательно вы должны убедиться, что все пол, которые используются как родительские ключи, имеют или ограничение PRIMARY KEY или ограничение UNIQUE, наподобие ограничения NOT NULL.ПЕРВИЧНЫЙ КЛЮЧ КАК УНИКАЛЬНЫЙ ВНЕШНИЙ КЛЮЧСсылка ваших внешних ключей только на первичные ключи, как мы это делали в типовых таблицах, - хороша стратеги. Когда вы используете внешние клю- чи, вы связываете их не просто с родительскими ключами на которые они ссылаются; вы связываете их с определенной строкой таблицы где этот родительс- кий ключ будет найден. Сам по себе родительский ключ не обеспечивает ника- кой информации которая бы не была уже представлена во внешнем ключе. Смысл, например, пол snum как внешнего ключа в таблице Заказчиков - это связь которую он обеспечивает, не к значению пол snum на которое он ссылается, а к другой информации в таблице Продавцов, такой например как, имена продавцов, их местоположение, и так далее. Внешний ключ - это не просто связь между двум идентичными значениями; это - связь, с помощью этих двух значений, между двум строками таблицы указанной в запросе. Это поле snum может использоваться чтобы связывать любую информацию в строке из таблицы Заказчиков со ссылочной строкой из таблицы Продавцов - например чтобы узнать - живут ли они в том же самом городе, кто имеет более длинное имя, имеет ли продавец кроме данного заказчика каких-то других заказчиков, и так далее. Так как цель первичного ключа состоит в том, чтобы идентифицировать уникальность строки, это более логичный и менее неоднозначный выбор для внешнего ключа. Для любого внешнего ключа который использует уникальный ключ как родительский ключ, вы должны создать внешний ключ который бы использовал первичный ключ той же самой таблицы для того же самого действия. Внешний ключ который не имеет никакой другой цели кроме связывания строк, напоминает первичный ключ используемый исключительно для идентификации строк, и является хорошим средством сохранить структуру вашей базы данных ясной и простой, и - следовательно создающей меньше трудностей.ОГРАНИЧЕНИЯ ВНЕШНЕГО КЛЮЧАВнешний ключ, в частности, может содержать только те значения которые фактически представлены в родительском ключе или пустые(NULL). Попытка ввести другие значения в этот ключ будет отклонена. Вы можете объявить внешний ключ как NOT NULL, но это необязательно, и в большинстве случаев, нежелательно. Например, предположим, что вы вводи- те заказчика не зная заранее, к какому продавцу он будет назначен. Лучший выход в этой ситуации, будет если использовать значение NOT NULL, которое должно быть изменено позже на конкретное значение.ЧТО СЛУЧИТСЯ, ЕСЛИ ВЫ ВЫПОЛНИТЕ КОМАНДУ МОДИФИКАЦИИДавайте условимся, что все внешние ключи созданные в наших таблицах приме- ров, объявлены и предписаны с ограничениями внешнего ключа, следующим образом :CREATE TABLE Salespeople (snum integer NOT NULL PRIMARY KEY, sname char(10) NOT NULL, city char(10), comm decimal); CREATE TABLE Customers (cnum integer NOT NULL PRIMARY KEY, cname char(10) NOT NULL, city char(10), rating integer, snum integer, FOREIGN KEY (snum) REFERENCES Salespeople, UNIQUE (cnum, snum) ; CREATE TABLE Orders (cnum integer NOT NULL PRIMARY KEY, amt decimal, odate date NOT NULL, cnum integer NOT NULL snum integer NOT NULL FOREIGN KEY (cnum, snum) REFERENCES CUSTOMERS (cnum, snum); ВКЛЮЧЕНИЕ ОПИСАНИЙ ТАБЛИЦЫИмеется несколько атрибутов таких определений о которых нужно поговорить. Причина по которой мы решили сделать пол cnum и snum в таблице Порядков, единым внешним ключом - это гарантия того, что для каждого заказчика содержащегося в порядках, продавец кредитующий этот порядок - тот же что и указанный в таблице Заказчиков. Чтобы создать такой внешний ключ, мы бы- ли бы должны поместить ограничение таблицы UNIQUE в два пол таблицы Заказчиков, даже если оно необязательно для самой этой таблицы. Пока поле cnum в этой таблица имеет ограничение PRIMARY KEY, оно будет уникально в любом случае, и следовательно невозможно получить еще одну комбинацию пол cnum с каким-то другим полем. Создание внешнего ключа таким способом поддерживает целостность базы данных, даже если при этом вам будет запрещено внутреннее прерывание по ошибке и кредитовать любого продавца, иного чем тот который назначен именно этому заказчику.С точки зрения поддержания целостности базы данных, внутренние прерывания ( или исключения) конечно же нежелательны. Если вы их допускаете и в то же врем хотите поддерживать целостность вашей базы данных, вы можете объявить пол snum и cnum в таблице Порядков независимыми внешними ключами этих полей в таблице Продавцов и таблице Заказчиков, соответственно. Фактически, использование пол snum в таблице Порядков, как мы это делали, необязательно, хотя это полезно было сделать для разнообразия. Поле cnum связывая каждый порядок заказчиков в таблице Заказчиков, в таблице Порядков и в таблице Заказчиков, должно всегда быть общим чтобы находить правильное поле snum для данного порядка ( не разрешая никаких исключений ). Это означает что мы записываем фрагмент информации - какой заказчик назначен к какому продавцу - дважды, и нужно будет выполнять дополнительную работу чтобы удостовериться, что обе версии согласуются. Если мы не имеем ограничения внешнего ключа как сказано выше, эта ситуация будет особенно проблематична, потому что каждый порядок нужно будет проверять вручную ( вместе с запросом ), чтобы удостовериться что именно соответствующий продавец кредитовал каждую соответствующую продажу. Наличие такого типа информационной избыточности в вашей базе данных, называется деморализация ( denormalization ), что нежелательно в идеальной реляционной базе данных, хотя практически и может быть разрешена. Деморализация может заставить некоторые запросы выполняться быстрее, поскольку запрос в одной таблице выполняется всегда значительно быстрее чем в объединении. ДЕЙСТВИЕ ОГРАНИЧЕНИЙКак такие ограничения воздействуют на возможность и невозможность Вами использовать команды модификации DML? Для полей, определен- ных как внешние ключи, ответ довольно простой: любые значения которые вы помещаете в эти пол с командой INSERT или UPDATE должны уже быть представлены в их родительских ключах. Вы можете помещать пустые(NULL) значения в эти пол, несмотря на то что значения NULL не позволительны в родительских ключах, если они имеют ограничение NOT NULL. Вы можете удалять (DELETE ) любые строки с внешними ключами не используя роди- тельские ключи вообще.Поскольку затронут вопрос об изменении значений родительского ключа, ответ, по определению ANSI, еще проще, но возможно несколько более ограничен: любое значение родительского ключа ссылаемого с помощью значения внешнего ключа, не может быть удалено или изменено. Это означает, например, что вы не можете удалить заказчика из таблицы Заказчиков пока он еще имеет порядки в таблице Порядков. В зависимости от того, как вы используете эти таблицы, это может быть или желательно или хлопотно. Однако - это конечно лучше чем иметь сис- тему, которая позволит вам удалить заказчика с текущими порядками и оставить таблицу Порядков ссылающейся на несуществующих заказчиков. Смысл этой системы ограничения в том, что создатель таблицы Порядков, используя таблицу Заказчиков и таблицу Продавцов как родительские клю- чи может наложить значительные ограничения на действия в этих таблицах. По этой причине, вы не сможете использовать таблицу которой вы не распоряжаетесь ( т.е. не вы ее создавали и не вы являетесь ее владельцем), по- ка владелец(создатель) этой таблицы специально не передаст вам на это право ( что объясняется в Главе 22). Имеются некоторые другие возможные действия изменения родительс- кого ключа, которые не являются частью ANSI, но могут быть найдены в некоторых коммерческих программах. Если вы хотите изменить или удалить текущее ссылочное значение родительского ключа, имеется по существу три возможности:
|
||||||
|