Первичный ключ - очень неудачное обозначение из-за коннотации «Первичный» и подсознательной ассоциации вследствие логической модели. Я таким образом избегаю использовать это. Вместо этого я ссылаюсь на суррогатный ключ физической модели и естественный ключ (и) логической модели.
Важно, чтобы логическая модель для каждой сущности имела хотя бы один набор «бизнес-атрибутов», составляющих ключ для сущности. Бойс, Кодд, Дейт и др. Ссылаются на них в реляционной модели как ключи-кандидаты. Когда мы затем создаем таблицы для этих сущностей, их ключи-кандидаты становятся естественными ключами в этих таблицах. Только с помощью этих Natural Keys пользователи могут однозначно идентифицировать строки в таблицах; так как суррогатные ключи всегда должны быть скрыты от пользователей. Это потому, что суррогатные ключи не имеют делового значения.
Однако физическая модель для наших таблиц во многих случаях будет неэффективной без суррогатного ключа. Напомним, что непокрытые столбцы для некластеризованного индекса можно найти (в общем случае) только через поиск ключей в кластеризованном индексе (на мгновение игнорируйте таблицы, реализованные в виде кучи). Когда наши доступные естественные ключи являются широкими, это (1) расширяет ширину наших некластеризованных конечных узлов, увеличивая требования к хранилищу и доступ к чтению для поиска и сканирования этого некластеризованного индекса; и (2) уменьшает разветвление нашего кластерного индекса, увеличивая высоту и размер индекса, снова увеличивая требования к чтению и хранилищу для наших кластеризованных индексов; и (3) увеличивает требования к кешу для наших кластерных индексов. погоня за другими индексами и данными вне кеша.
Здесь полезен небольшой суррогатный ключ, обозначенный для СУРБД как «первичный ключ». При установке в качестве ключа кластеризации для использования при поиске ключей в кластеризованном индексе из некластеризованных индексов и поисках внешнего ключа из связанных таблиц все эти недостатки исчезают. Наши разветвления кластеризованных индексов снова увеличиваются, чтобы уменьшить высоту и размер кластеризованных индексов, уменьшить нагрузку на кэш для наших кластеризованных индексов, уменьшить чтение при доступе к данным с помощью любого механизма (будь то сканирование индекса, поиск индекса, поиск некластеризованного ключа или поиск внешнего ключа) и уменьшить требования к хранилищу как для кластеризованных, так и некластеризованных индексов наших таблиц.
Обратите внимание, что эти преимущества имеют место только тогда, когда суррогатный ключ является и маленьким, и ключом кластеризации. Если в качестве ключа кластеризации используется GUID, ситуация часто будет хуже, чем если бы использовался наименьший доступный естественный ключ. Если таблица организована как куча, то для поиска ключей будет использоваться 8-байтовый (куча) RowID, который лучше, чем 16-байтовый GUID, но менее производительный, чем 4-байтовое целое число.
Если GUID должен использоваться из-за бизнес-ограничений, тогда поиск лучшего ключа кластеризации имеет смысл. Если, например, возможны небольшой идентификатор сайта и 4-байтовый «номер последовательности сайта», то такой дизайн может дать лучшую производительность, чем GUID в качестве суррогатного ключа.
Если последствия кучи (возможно, хеш-соединения) делают это предпочтительным хранилищем, тогда затраты на более широкий ключ кластеризации должны быть сбалансированы в анализе компромисса.
Рассмотрим этот пример ::
ALTER TABLE Persons
ADD CONSTRAINT pk_PersonID PRIMARY KEY (P_Id,LastName)
, где кортеж " (P_Id, LastName) " требует ограничения уникальности и может быть длинным Unicode LastName плюс 4-байтовым целым числом, было бы желательно (1) декларативно применять это ограничение как " ADD CONSTRAINT pk_PersonID UNIQUE NONCLUSTERED (P_Id, LastName) " и (2) отдельно объявить небольшой суррогатный ключ как " первичный ключ " кластерного индекса. Стоит отметить, что Анита, возможно, только желает добавить LastName к этому ограничению, чтобы сделать это покрытым полем, которое не нужно в кластеризованном индексе, потому что оно покрывает ВСЕ поля.
Возможность в SQL Server назначать первичный ключ как некластеризованный является неблагоприятным историческим обстоятельством из-за соотношения значения «предпочтительный натуральный ключ или ключ-кандидат» (из логической модели) со значением «ключ поиска в хранилище» из физической модели. Насколько я понимаю, первоначально SYBASE SQL Server всегда использовал 4-байтовый RowID, будь то в куче или кластерном индексе, в качестве «ключа поиска в хранилище» из физической модели.