Мартин Грубер - Понимание SQL (991940), страница 32
Текст из файла (страница 32)
Родительский ключ должен быть структурен, чтобы гарантировать, что каждое значение внешнего ключа будет соответствовать одной указаннойстроке. Это означает, что он (ключ) должен быть уникальным и не содержать никакихпустых значений (NULL). Этого не достаточно для родительского ключа в случае выполнения такого требования как при объявлении внешнего ключа.
SQL должен бытьуверен что двойные значения или пустые значения (NULL) не были введены в родительский ключ. Следовательно вы должны убедиться, что все поля, которые исполь-зуются как родительские ключи, имеют или ограничение PRIMARY KEY илиограничение UNIQUE, наподобии ограничения NOT NULL.ПЕРВИЧНЫЙ КЛЮЧ КАК УНИКАЛЬНЫЙ ВНЕШНИЙ КЛЮЧСсылка ваших внешних ключей только на первичные ключи, как мы это делали втиповых таблицах, — хорошая стратегия. Когда вы используете внешние ключи, высвязываете их не просто с родительскими ключами, на которые они ссылаются; высвязываете их с определенной строкой таблицы, где этот родительский ключ будетнайден.
Сам по себе родительский ключ не обеспечивает никакой информации, которая бы не была уже представлена во внешнем ключе. Смысл, например, поля snumкак внешнего ключа в таблице Заказчиков — это связь, которую он обеспечивает, не кзначению поля snum, на которое он ссылается, а к другой информации в таблицеПродавцов, такой например как имена продавцов, их местоположение, и так далее.Внешний ключ — это не просто связь между двумя идентичными значениями; это —связь, с помощью этих двух значений, между двумя строками таблицы указанной в запросе.Это поле snum может использоваться чтобы связывать любую информацию встроке из таблицы Заказчиков со ссылочной строкой из таблицы Продавцов — например чтобы узнать — живут ли они в том же самом городе, кто имеет более длинноеимя, имеет ли продавец кроме данного заказчика каких-то других заказчиков, и такдалее.Так как цель первичного ключа состоит в том, чтобы идентифицировать уникальность строки, это более логичный и менее неоднозначный выбор для внешнего ключа.Для любого внешнего ключа который использует уникальный ключ как родительскийключ, вы должны создать внешний ключ который бы использовал первичный ключ тойже самой таблицы для того же самого действия.
Внешний ключ который не имеет никакой другой цели кроме связывания строк, напоминает первичный ключ используемый исключительно для идентификации строк, и является хорошим средствомсохранить структуру вашей базы данных ясной и простой, и — следовательно создающей меньше трудностей.ОГРАНИЧЕНИЯ ВНЕШНЕГО КЛЮЧАВнешний ключ, в частности, может содержать только те значения которые фактически представлены в родительском ключе или пустые (NULL).
Попытка ввести другие значения в этот ключ будет отклонена.Вы можете обьявить внешний ключ как NOT NULL, но это необязательно, и вбольшинстве случаев, нежелательно. Например, предположим, что вы вводите заказчика не зная заранее, к какому продавцу он будет назначен. Лучший выход в этой ситуации будет, если использовать значение NOT NULL, которое должно быть измененопозже на конкретное значение.ЧТО СЛУЧИТСЯ, ЕСЛИ ВЫ ВЫПОЛНИТЕ КОМАНДУМОДИФИКАЦИИДавайте условимся, что все внешние ключи созданые в наших таблицах примеров, обьявлены и предписаны с ограничениями внешнего ключа, следующим образом:CREATE(snumsnamecitycommTABLE Salespeopleinteger NOT NULL PRIMARY KEY,char(10) NOT NULL,char(10),decimal);CREATE TABLE Customers(cnuminteger NOT NULL PRIMARY KEY,cname char(10) NOT NULL,citychar(10),rating integer,snuminteger,FOREIGN KEY (snum) REFERENCES Salespeople,UNIQUE (cnum, snum));CREATE TABLE Orders(cnum integer NOT NULL PRIMARY KEY,amtdecimal,odate date NOT NULL,cnum integer NOT NULLsnum integer NOT NULLFOREIGN KEY (cnum, snum) REFERENCESCUSTOMERS (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, но могут быть найдены в некоторых коммерческих программах. Если вы хотите изменить или удалить текущее ссылочное значениеродительского ключа, имеется по существу три возможности:* Вы можете ограничить, или запретить, изменение (способом ANSI), обозначив,что изменения в родительском ключе — ограничены.* Вы можете сделать изменение в родительском ключе и тем самым сделать изменения во внешнем ключе автоматическим, что называется — каскадным изменением.* Вы можете сделать изменение в родительском ключе, и установить внешнийключ в NULL автоматически (полагая, что NULL разрешен во внешнем ключе), что называется — пустым изменением внешнего ключа.Даже в пределах этих трех категорий, вы можете не захотеть обрабатывать всекоманды модификации таким способом.