Могу ли я иметь несколько первичных ключей в одной таблице? - PullRequest
349 голосов
/ 20 октября 2008

Могу ли я иметь несколько первичных ключей в одной таблице?

Ответы [ 12 ]

508 голосов
/ 20 октября 2008

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

CREATE TABLE userdata (
  userid INT,
  userdataid INT,
  info char(200),
  primary key (userid, userdataid)
);

Обновление: Вот ссылка с более подробным описанием составных первичных ключей.

179 голосов
/ 20 октября 2008

У вас может быть только один первичный ключ, но в первичном ключе может быть несколько столбцов.

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

34 голосов
/ 22 октября 2008

Таблица может иметь несколько ключей-кандидатов. Каждый ключ-кандидат представляет собой столбец или набор столбцов, которые являются УНИКАЛЬНЫМИ, взятыми вместе, а также NOT NULL. Таким образом, достаточно указать значения для всех столбцов любого ключа-кандидата, чтобы определить, что есть одна строка, соответствующая критериям, или вообще нет строк.

Ключи-кандидаты являются фундаментальной концепцией в реляционной модели данных.

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

Я рекомендую эти методы, но в реляционной модели нет ничего, что требовало бы выбора первичного ключа среди возможных ключей.

13 голосов
/ 17 июня 2011

Это ответ и на главный вопрос, и на вопрос Кальми о

Какой смысл иметь несколько автоматически генерирующих столбцов?

Этот код ниже имеет составной первичный ключ. Один из его столбцов автоматически увеличивается. Это будет работать только в MyISAM. InnoDB сгенерирует ошибку " ОШИБКА 1075 (42000): неверное определение таблицы; может быть только один автоматический столбец, и его необходимо определить как ключ ".

DROP TABLE IF EXISTS `test`.`animals`;
CREATE TABLE  `test`.`animals` (
  `grp` char(30) NOT NULL,
  `id` mediumint(9) NOT NULL AUTO_INCREMENT,
  `name` char(30) NOT NULL,
  PRIMARY KEY (`grp`,`id`)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Which returns:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+
7 голосов
/ 04 апреля 2016

Первичный ключ - очень неудачное обозначение из-за коннотации «Первичный» и подсознательной ассоциации вследствие логической модели. Я таким образом избегаю использовать это. Вместо этого я ссылаюсь на суррогатный ключ физической модели и естественный ключ (и) логической модели.

Важно, чтобы логическая модель для каждой сущности имела хотя бы один набор «бизнес-атрибутов», составляющих ключ для сущности. Бойс, Кодд, Дейт и др. Ссылаются на них в реляционной модели как ключи-кандидаты. Когда мы затем создаем таблицы для этих сущностей, их ключи-кандидаты становятся естественными ключами в этих таблицах. Только с помощью этих 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, будь то в куче или кластерном индексе, в качестве «ключа поиска в хранилище» из физической модели.

6 голосов
/ 17 июня 2011

Как отмечают другие, возможно иметь многоколонные первичные ключи. Однако следует отметить, что если у вас есть функциональные зависимости , которые не вводятся ключом, вам следует рассмотреть нормализацию вашего отношения.

Пример:

Person(id, name, email, street, zip_code, area)

Может существовать функциональная зависимость между id -> name,email, street, zip_code and area Но часто zip_code ассоциируется с area, и поэтому существует внутренняя функциональная зависимость между zip_code -> area.

Таким образом, можно рассмотреть возможность разбить его на другую таблицу:

Person(id, name, email, street, zip_code)
Area(zip_code, name)

Так что это согласуется с третьей нормальной формой .

5 голосов
/ 08 апреля 2018

(много их изучал)

Может быть только 1 первичный ключ.
Но вы можете иметь несколько альтернативных ключей.

Проще говоря, это так:

  • Там может быть кратным Ключи-кандидаты (минимальные столбцы для уникальной идентификации строки) в таблице.

    • Один из возможных ключей выбран специально и называется Первичный ключ
    • Все другие возможные ключи называются Альтернативные ключи
      • И первичный, и альтернативный ключи могут быть Составной ключ с (2 или более столбцов)

Источники:
https://en.wikipedia.org/wiki/Superkey
https://en.wikipedia.org/wiki/Candidate_key
https://en.wikipedia.org/wiki/Primary_key
https://en.wikipedia.org/wiki/Compound_key

2 голосов
/ 30 сентября 2017

Наличие двух первичных ключей одновременно невозможно. Но (при условии, что вы не перепутали случай с составным ключом), может потребоваться сделать один атрибут уникальным.

CREATE t1(
c1 int NOT NULL,
c2 int NOT NULL UNIQUE,
...,
PRIMARY KEY (c1)
);

Однако обратите внимание, что в реляционной базе данных «суперключ» - это подмножество атрибутов, которые однозначно идентифицируют кортеж или строку в таблице. «Ключ» - это «супер ключ», который имеет дополнительное свойство, которое удаляет любой атрибут из ключа, делает этот ключ больше не «супер ключом» (или просто «ключ» является минимальным супер ключом). Если ключей больше, все они являются ключами-кандидатами. Мы выбираем один из ключей-кандидатов в качестве первичного ключа. Вот почему разговор о нескольких первичных ключах для одного отношения или таблицы является конфликтом.

2 голосов
/ 28 мая 2013

Первичный ключ - это ключ, который однозначно идентифицирует запись и используется во всех индексах. Вот почему вы не можете иметь более одного. Это также обычно ключ, который используется при присоединении к дочерним таблицам, но это не является обязательным требованием. Настоящая цель PK - убедиться, что что-то позволяет однозначно идентифицировать запись, чтобы изменения данных влияли на правильную запись и чтобы можно было создавать индексы.

Однако вы можете поместить несколько полей в один первичный ключ (составной PK). Это сделает ваши объединения медленнее (особенно, если они являются полями строкового типа большего размера) и ваши индексы будут больше, но это может избавить от необходимости выполнять объединения в некоторых дочерних таблицах, так что, если речь идет о производительности и дизайне, сделайте это на основа дела. Когда вы делаете это, каждое поле само по себе не уникально, но их комбинация есть. Если одно или несколько полей в составном ключе также должны быть уникальными, вам нужен уникальный индекс для него. Вполне вероятно, что если одно поле является уникальным, это лучший кандидат для ПК.

Теперь у вас есть несколько кандидатов на ПК. В этом случае вы выбираете один из них в качестве PK или используете суррогатный ключ (я лично предпочитаю суррогатные ключи для этого экземпляра). И (это очень важно!) Вы добавляете уникальные индексы к каждому из ключей-кандидатов, которые не были выбраны в качестве PK. Если данные должны быть уникальными, им нужен уникальный индекс, является ли он PK или нет. Это проблема целостности данных. (Обратите внимание, что это также верно для любого случая, когда вы используете суррогатный ключ; у людей возникают проблемы с суррогатными ключами, потому что они забывают создать уникальные индексы для ключей-кандидатов.)

Бывают случаи, когда вам требуется более одного суррогатного ключа (обычно это PK, если он у вас есть). В этом случае вам нужно больше не PK, а больше полей с автоматически сгенерированными ключами. Большинство БД этого не допускают, но есть способы обойти это. Сначала подумайте, можно ли рассчитать второе поле на основе первого автоматически сгенерированного ключа (например, Field1 * -1) или, возможно, необходимость во втором автоматически сгенерированном ключе действительно означает, что вы должны создать связанную таблицу. Связанные таблицы могут быть в отношении один к одному. Вы бы принудительно это применили, добавив PK из родительской таблицы в дочернюю таблицу, а затем добавив новое автоматически сгенерированное поле в таблицу, а затем все поля, подходящие для этой таблицы. Затем выберите один из двух ключей в качестве PK и поместите уникальный индекс в другой (автоматически сгенерированное поле не должно быть PK). И обязательно добавьте FK в поле, которое находится в родительской таблице. В общем, если у вас нет дополнительных полей для дочерней таблицы, вам нужно выяснить, почему вы считаете, что вам нужны два автоматически сгенерированных поля.

2 голосов
/ 20 октября 2008

Некоторые люди используют термин «первичный ключ» для обозначения именно целочисленного столбца, который получает свои значения, сгенерированные каким-то автоматическим механизмом. Например, AUTO_INCREMENT в MySQL или IDENTITY в Microsoft SQL Server. Вы используете первичный ключ в этом смысле?

Если так, ответ зависит от марки базы данных, которую вы используете. В MySQL вы не можете сделать это, вы получаете сообщение об ошибке:

mysql> create table foo (
  id int primary key auto_increment, 
  id2 int auto_increment
);
ERROR 1075 (42000): Incorrect table definition; 
there can be only one auto column and it must be defined as a key

В базе данных некоторых других марок вы можете определить более одного автоматически генерируемого столбца в таблице.

...