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

Ответы:

559

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

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

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

Адам Пирс
источник
2
В этом примере ОБА ИД пользователя и идентификатор пользователя необходимы для идентификации / поиска уникальной строки. Не уверен, каково было намерение ОП, но я пришел сюда, чтобы посмотреть, смогу ли я однозначно идентифицировать строку с одним из набора ключей. Например, я хотел бы идентифицировать уникального пользователя либо с именем пользователя, либо с идентификатором пользователя, без необходимости того и другого. Я предполагаю, что ответ RB уникальных индексов добился бы цели там.
Буррито
1
@Benitok Как упоминалось в ответе RB. , Вы можете использовать уникальные индексы, чтобы делать то, что вы ищете (уникальный индексированный столбец, независимый от других уникальных индексированных столбцов в той же таблице). Обязательно проконсультируйтесь с вашим конкретным руководством по SQL для подробностей о точном используемом синтаксисе языка.
утра
195

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

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

RB.
источник
39

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

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

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

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

Уолтер Митти
источник
5
Согласовано. Все ключи равны (ни один не является «основным») в логической модели. Выбор того, какой ключ в физической реализации получает обозначение PRIMARY KEY, является произвольным и зависит от поставщика / продукта.
понедельник,
3
Я бы сказал, что это зависит от разработчика базы данных.
Уолтер Митти
Я только что натолкнулся на случай использования, где это требуется. У меня есть таблица, которая будет создаваться / управляться Entity Framework - которая, насколько я могу собрать, не поддерживает уникальные составные ограничения не первичного ключа в настоящее время. Однако он поддерживает составные первичные ключи. Данные также будут связаны с удаленной системой баз данных, которая вообще не поддерживает составные ключи. Я пошел с созданием Composite PK в EF, а также с добавлением необнуляемого столбца GUID, который другая система может использовать для уникальной идентификации.
Крис Невилл
2
Крис, я сказал, что реляционная модель не требует первичных ключей. Я ничего не сказал о том, может ли какой-то инструмент потребовать их. Но я понимаю вашу точку зрения.
Уолтер Митти
Я думаю, что существует требование, чтобы PK был минимальным, то есть использовал наименьшее количество столбцов для уникальной идентификации каждой записи.
Гэри
14

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

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

Этот код ниже имеет составной первичный ключ. Один из его столбцов автоматически увеличивается. Это будет работать только в 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 |
+--------+----+---------+
глаз
источник
2
Это работает, если вы сначала указали автоинкрементный столбец в определении первичного ключа. (Может быть, это изменилось, я только что проверил это в 5.6)
CTarczon
11

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

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

  • В таблице может существовать несколько ключей-кандидатов .
    • Primary KEY - только один из выбранных нами ключей-кандидатов.
    • Альтернативные ключи - все остальные ключи-кандидаты
      • И первичный, и альтернативный ключи могут быть составными ключами

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

Манохар Редди Поредди
источник
6

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

Пример:

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

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

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

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

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

Еще один Компьютерщик
источник
6

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

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

Питер Гиркенс
источник
3
Можете ли вы перевести это на английский!
Джасир
3

Некоторые люди используют термин «первичный ключ» для обозначения в точности целочисленного столбца, который получает свои значения, сгенерированные каким-то автоматическим механизмом. Например, 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

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

Билл Карвин
источник
5
Какой смысл иметь несколько автоматически генерирующих столбцов?
Tarnay Kálmán
Я не имею в виду сценарий использования, но если бы возникла необходимость, некоторые бренды баз данных поддерживали бы это, а некоторые - нет. Это все, что я говорю.
Билл Карвин
1
Вот случай: в таблице заказов у ​​меня есть и идентификатор (с автоинкрементом), и внешний идентификатор (строки типа хэша), оба должны быть уникальными, поэтому теоретически вы можете сказать, что они оба «первичные». конечно, это можно сделать с помощью вторичного уникального индекса, но все же это законный случай (ИМХО)
Nir
2

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

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

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

Русиру Адитья Самарасингхе
источник
В Википедии нет определения «ключ». Кроме того, «удаление какого-либо атрибута из ключа делает этот ключ больше не« супер-ключом »» для меня ничего не значило, так как при удалении атрибута из супер-ключа все же может быть супер-ключ.
Манохар Редди Поредди
@ManoharReddyPoreddy Да, в этом случае ваш набор атрибутов - это не «ключ», а «супер ключ». Я имею в виду, что если набор атрибутов является «ключом», набор должен быть минимальным, или набор должен иметь дополнительное свойство, которое при удалении любого атрибута из набора делает результирующий набор не более «супер-ключом».
Русиру Адития Самарасингхе
Похоже, что ваше фактическое значение «ключ» - это Candidate_key ( en.wikipedia.org/wiki/Candidate_key ), возможно, это следует упомянуть.
Манохар Редди Поредди
@ManoharReddyPoreddy Да, я уже упоминал об этом в своем ответе. «Если ключей больше, все они являются ключами-кандидатами». В любом случае спасибо за ваш отзыв.
Русиру Адития Самарасингх
1. Когда вы упоминаете «Если ключей больше, все они являются ключами-кандидатами», ... Вы имеете в виду иначе / иначе, они не являются ключами-кандидатами? ... 2. Где остальная часть? ... Мы вообще одна страница?
Манохар Редди Поредди
1

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

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

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

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

HLGEM
источник
0

Хорошие технические ответы были даны лучше, чем я могу. Я только могу добавить в эту тему:

Если вы хотите что-то, что не разрешено / приемлемо, это хороший повод сделать шаг назад.

  1. Понять суть, почему это не приемлемо.
  2. Копайте больше в документации / журнальных статьях / сети и т. Д.
  3. Проанализируйте / рассмотрите текущий дизайн и укажите основные недостатки.
  4. Рассмотрите и протестируйте каждый шаг во время нового дизайна.
  5. Всегда смотрите вперед и старайтесь создать адаптивное решение.

Надеюсь, это поможет кому-то.

Том лайм
источник
1
общий (хотя и полезный) совет, а не ответ на конкретный вопрос.
Брэдфорд Нидхем
-3

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

CREATE TABLE CHAPTER (
    BOOK_ISBN VARCHAR(50) NOT NULL,
    IDX INT NOT NULL,
    TITLE VARCHAR(100) NOT NULL,
    NUM_OF_PAGES INT,
    PRIMARY KEY (BOOK_ISBN, IDX)
);
Stema
источник
Таблица SQL может иметь только один PK.
Philipxy