Могу ли я иметь несколько первичных ключей в одной таблице?
Могу ли я иметь несколько первичных ключей в одной таблице?
Ответы:
Таблица может иметь составной первичный ключ, который является первичным ключом, состоящим из двух или более столбцов. Например:
CREATE TABLE userdata (
userid INT,
userdataid INT,
info char(200),
primary key (userid, userdataid)
);
Обновление: вот ссылка с более подробным описанием составных первичных ключей.
У вас может быть только один первичный ключ, но у вас может быть несколько столбцов в первичном ключе.
Вы также можете иметь уникальные индексы в своей таблице, которые будут работать немного как первичный ключ в том, что они будут приводить в исполнение уникальные значения и ускорять запросы этих значений.
Таблица может иметь несколько ключей-кандидатов. Каждый ключ-кандидат представляет собой столбец или набор столбцов, которые являются УНИКАЛЬНЫМИ, взятыми вместе, а также NOT NULL. Таким образом, указание значений для всех столбцов любого ключа-кандидата достаточно для определения того, что есть одна строка, соответствующая критериям, или вообще нет строк.
Ключи-кандидаты являются фундаментальной концепцией в реляционной модели данных.
Обычно, если в одной таблице присутствует несколько ключей, один из ключей-кандидатов назначается первичным ключом. Также обычной практикой является использование любых внешних ключей в таблице для ссылки на первичный ключ, а не на любой другой ключ-кандидат.
Я рекомендую эти методы, но в реляционной модели нет ничего, что требовало бы выбора первичного ключа среди ключей-кандидатов.
Это ответ как на главный вопрос, так и на вопрос @ Калми о
Какой смысл иметь несколько автоматически генерирующих столбцов?
Этот код ниже имеет составной первичный ключ. Один из его столбцов автоматически увеличивается. Это будет работать только в 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 или более столбцов.
Источники:
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
Как отмечают другие, возможно иметь многоколонные первичные ключи. Однако следует отметить, что если у вас есть некоторые функциональные зависимости , которые не вводятся ключом, вы должны рассмотреть вопрос о нормализации вашего отношения.
Пример:
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)
Так что это соответствует третьей нормальной форме .
Первичный ключ - очень неудачное обозначение из-за коннотации «Первичный» и подсознательной ассоциации вследствие логической модели. Я таким образом избегаю использовать это. Вместо этого я ссылаюсь на суррогатный ключ физической модели и естественный ключ (и) логической модели.
Важно, чтобы логическая модель для каждой сущности имела как минимум один набор «бизнес-атрибутов», составляющих ключ для сущности. Бойс, Кодд, Дейт и др. Ссылаются на них в реляционной модели как ключи-кандидаты. Когда мы затем создаем таблицы для этих сущностей, их ключи-кандидаты становятся естественными ключами в этих таблицах. Только с помощью этих 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, будь то в куче или кластерном индексе, в качестве «ключа поиска в хранилище» из физической модели.
Некоторые люди используют термин «первичный ключ» для обозначения в точности целочисленного столбца, который получает свои значения, сгенерированные каким-то автоматическим механизмом. Например, 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
В базе данных некоторых других брендов вы можете определить более одного автоматически генерируемого столбца в таблице.
Наличие двух первичных ключей одновременно невозможно. Но (при условии, что вы не перепутали случай с составным ключом), может потребоваться сделать один атрибут уникальным.
CREATE t1(
c1 int NOT NULL,
c2 int NOT NULL UNIQUE,
...,
PRIMARY KEY (c1)
);
Однако обратите внимание, что в реляционной базе данных «суперключ» - это подмножество атрибутов, которые однозначно идентифицируют кортеж или строку в таблице. «Ключ» - это «супер ключ», который имеет дополнительное свойство, которое удаляет любой атрибут из ключа, делает этот ключ больше не «супер ключом» (или просто «ключ» является минимальным супер ключом). Если ключей больше, все они являются ключами-кандидатами. Мы выбираем один из ключей-кандидатов в качестве первичного ключа. Вот почему разговор о нескольких первичных ключах для одного отношения или таблицы является конфликтом.
Первичный ключ - это ключ, который уникальным образом идентифицирует запись и используется во всех индексах. Вот почему вы не можете иметь более одного. Это также обычно ключ, который используется при присоединении к дочерним таблицам, но это не является обязательным требованием. Настоящая цель PK - убедиться, что что-то позволяет однозначно идентифицировать запись, чтобы изменения данных влияли на правильную запись и чтобы можно было создавать индексы.
Однако вы можете поместить несколько полей в один первичный ключ (составной PK). Это сделает ваши объединения медленнее (особенно, если они являются полями строкового типа большего размера) и ваши индексы будут больше, но это может избавить от необходимости выполнять объединения в некоторых дочерних таблицах, что касается производительности и дизайна, рассматривайте их в основа дела. Когда вы делаете это, каждое поле само по себе не уникально, но их комбинация есть. Если одно или несколько полей в составном ключе также должны быть уникальными, вам нужен уникальный индекс для него. Вполне вероятно, что если одно поле является уникальным, это лучший кандидат для ПК.
Теперь у вас есть несколько кандидатов на ПК. В этом случае вы выбираете один из них в качестве PK или используете суррогатный ключ (я лично предпочитаю суррогатные ключи для этого экземпляра). И (это очень важно!) Вы добавляете уникальные индексы к каждому из ключей-кандидатов, которые не были выбраны в качестве PK. Если данные должны быть уникальными, им нужен уникальный индекс, является ли он PK или нет. Это проблема целостности данных. (Обратите внимание, что это также верно при каждом использовании суррогатного ключа; у людей возникают проблемы с суррогатными ключами, потому что они забывают создавать уникальные индексы для ключей-кандидатов.)
Бывают случаи, когда вам требуется более одного суррогатного ключа (обычно это PK, если он у вас есть). В этом случае вам нужно больше не PK, а больше полей с автоматически сгенерированными ключами. Большинство БД этого не допускают, но есть способы обойти это. Сначала подумайте, можно ли рассчитать второе поле на основе первого автоматически сгенерированного ключа (например, Field1 * -1) или, возможно, необходимость во втором автоматически сгенерированном ключе действительно означает, что вы должны создать связанную таблицу. Связанные таблицы могут быть в отношении один к одному. Вы бы принудительно применили это, добавив PK из родительской таблицы в дочернюю таблицу, а затем добавив новое автоматически сгенерированное поле в таблицу, а затем все поля, подходящие для этой таблицы. Затем выберите один из двух ключей в качестве PK и поместите уникальный индекс в другой (автоматически сгенерированное поле не обязательно должно быть PK). И обязательно добавьте FK в поле, которое находится в родительской таблице. В общем, если у вас нет дополнительных полей для дочерней таблицы, вам нужно выяснить, почему вы считаете, что вам нужны два автоматически сгенерированных поля.
Хорошие технические ответы были даны лучше, чем я могу. Я только могу добавить в эту тему:
Если вы хотите что-то, что не разрешено / приемлемо, это хороший повод сделать шаг назад.
Надеюсь, это поможет кому-то.
Да, это возможно в 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)
);