Первичный ключ или уникальный индекс?

127

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

Я разрабатываю новую базу данных для нового проекта, и у меня возникает дилемма:

В теории БД первичный ключ является фундаментальным элементом, это нормально, но в РЕАЛЬНЫХ проектах каковы преимущества и недостатки обоих?

Что вы используете в проектах?

РЕДАКТИРОВАТЬ: ... а как насчет первичных ключей и репликации на сервере MS SQL?

Cicik
источник
2
Здесь обсуждаются некоторые дополнительные соображения (хотя и с дополнительным контекстом покрывающего индекса) - dba.stackexchange.com/questions/21554/…
StuartLC
ПРИМЕЧАНИЕ. SQLite отличается тем, что допускает нулевое значение первичного ключа по сравнению с общим стандартом из-за устаревшей проблемы. sqlite.org/lang_createtable.html
bitinn

Ответы:

168

Что такое уникальный индекс?

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

СОЗДАТЬ ТАБЛИЦУ table1 (foo int, bar int);
СОЗДАТЬ УНИКАЛЬНЫЙ ИНДЕКС ux_table1_foo ON table1 (foo); - Создать уникальный индекс на foo.

INSERT INTO table1 (foo, bar) VALUES (1, 2); -- ХОРОШО
INSERT INTO table1 (foo, bar) VALUES (2, 2); -- ХОРОШО
INSERT INTO table1 (foo, bar) VALUES (3, 1); -- ХОРОШО
INSERT INTO table1 (foo, bar) VALUES (1, 4); - Не получается!

Повторяющаяся запись '1' для ключа 'ux_table1_foo'

Последняя вставка не удалась, потому что она нарушает уникальный индекс столбца, fooкогда пытается вставить значение 1 в этот столбец во второй раз.

В MySQL уникальное ограничение допускает несколько значений NULL.

Можно создать уникальный индекс для нескольких столбцов.

Первичный ключ против уникального индекса

То же самое:

  • Первичный ключ подразумевает уникальный индекс.

Вещи, которые разные:

  • Первичный ключ также подразумевает NOT NULL, но уникальный индекс может иметь значение NULL.
  • Может быть только один первичный ключ, но может быть несколько уникальных индексов.
  • Если кластеризованный индекс не определен, то первичный ключ будет кластеризованным индексом.
Марк Байерс
источник
4
Обратите внимание, что уникальный индекс - это индекс для столбца , не совсем точен, поскольку один уникальный индекс или первичный ключ может включать более одного столбца.
Alex Jasmin
2
@Alexandre Jasmin: Исправлено спасибо. Часть о нескольких столбцах упоминается позже.
Марк Байерс,
Что касается значений NULL, стандарты ansi допускают использование нескольких значений NULL в наборе данных с уникальным ограничением на него, и это также реализация в Oracle и PostgreSQL. Я считаю, что SQL Server допускает только одно нулевое значение.
Дэвид Олдридж,
3
но все же я не понял, например, когда использовать первичный ключ или когда использовать уникальный индекс? или могут быть оба в одинаковых ситуациях.
Амит
33

Вы можете увидеть это так:

Первичный ключ уникален

Уникальное значение не обязательно должно быть представлением элемента.

Смысл?; Ну, первичный ключ используется для идентификации элемента, если у вас есть «Человек», вы хотели бы иметь личный идентификационный номер (SSN или такой), который является основным для вашего человека.

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

У меня всегда есть первичные ключи, даже в таблицах отношений (средняя таблица / таблица соединений) они могут быть у меня. Зачем? Что ж, мне нравится следовать стандарту при кодировании, если "Человек" имеет идентификатор, а Автомобиль имеет идентификатор, тогда Человек -> Автомобиль также должен иметь идентификатор!

Филип Экберг
источник
В ваших таблицах отношений: вы имеете в виду, что вводите новый столбец с искусственным первичным ключом (например, целым числом) или вы используете составной первичный ключ (person_id, car_id)?
3
первичный ключ (person_id, car_id) будет лучшим. Но я обычно создаю новый столбец, конечно, это дает некоторые накладные расходы, но я считаю, что это хорошо. Вы никогда не знаете, хотите ли вы установить связь с определенным отношением в более позднем сценарии.
Филип Экберг
1
Еще одна вещь, которую суррогатный первичный ключ делает для вашей составной / объединенной таблицы, - это упрощение обслуживания ручных задач.
Роберт С. Барт,
2
Вам нужен только первичный ключ, если вы собираетесь иметь детей. Зачем добавлять столбец и последовательность, если значение нигде не появляется, если значение не используется ни для чего? Это подделка, чтобы не дать Access запрашивать ПК. Сделайте ПК, если вам нужно идентифицировать запись у ребенка, иначе это пустая трата.
3
Если это не имеет отношения к отношениям, то при чем тут? Вы указываете на поле и говорите, что это первичное. И? Что тогда происходит? А если нет естественного pk, я добавляю столбец, последовательность и триггер, и все потому, что ____? Некоторым просто нужно быть Первоначальным. Я избегаю правил без причины.
10

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

Ограничение FOREIGN KEY не обязательно должно быть связано только с ограничением PRIMARY KEY в другой таблице; его также можно определить для ссылки на столбцы ограничения UNIQUE в другой таблице

Для репликации транзакций вам понадобится первичный ключ. Из книг в Интернете:

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

Оба ответа предназначены для SQL Server 2005.

Джонас Линкольн
источник
ЭТО меня до чертиков пугает (первая цитата). Зачем? У меня есть таблица людей с произвольным идентификатором, это мой ПК, но я решил добавить Великобританию к телефону, электронной почте и SSN ... так что теперь 4 разных таблицы присоединяются к человеку в 4 разных столбцах? Я думаю, что я бы отказался от любой гибкости, которую вы могли бы получить для согласованности.
5

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

В качестве примера у меня есть следующие таблицы:

CREATE TABLE toll_booths (
    id            INTEGER       NOT NULL PRIMARY KEY,
    name          VARCHAR(255)  NOT NULL,
    ...
    UNIQUE(name)
)

CREATE TABLE cars (
    vin           VARCHAR(17)   NOT NULL PRIMARY KEY,
    license_plate VARCHAR(10)   NOT NULL,
    ...
    UNIQUE(license_plate)
)

CREATE TABLE drive_through (
    id            INTEGER       NOT NULL PRIMARY KEY,
    toll_booth_id INTEGER       NOT NULL REFERENCES toll_booths(id),
    vin           VARCHAR(17)   NOT NULL REFERENCES cars(vin),
    at            TIMESTAMP     DEFAULT CURRENT_TIMESTAMP NOT NULL,
    amount        NUMERIC(10,4) NOT NULL,
    ...
    UNIQUE(toll_booth_id, vin)
)

У нас есть две таблицы сущностей ( toll_boothsи cars) и таблица транзакций ( drive_through). В toll_boothтаблице используется суррогатный ключ, поскольку он не имеет естественного атрибута, изменение которого не гарантируется (имя можно легко изменить). В carsтаблице используется естественный первичный ключ, потому что он имеет неизменяемый уникальный идентификатор ( vin). Таблица drive_throughтранзакций использует суррогатный ключ для легкой идентификации, но также имеет уникальное ограничение на атрибуты, которые гарантированно будут уникальными во время вставки записи.

На http://database-programmer.blogspot.com есть несколько отличных статей по этой конкретной теме.

aekeus
источник
4

У первичных ключей нет недостатков.

Чтобы добавить только некоторую информацию к ответам @MrWiggles и @Peter Parker, когда таблица не имеет первичного ключа, например, вы не сможете редактировать данные в некоторых приложениях (в конечном итоге они скажут, что не могут редактировать / удалять данные без основной ключ). Postgresql позволяет нескольким значениям NULL находиться в столбце UNIQUE, PRIMARY KEY не допускает значений NULL. Также некоторые ORM, генерирующие код, могут иметь проблемы с таблицами без первичных ключей.

ОБНОВИТЬ:

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

ЭМПИ
источник
Когда вставляются новые строки или обновляется этот столбец, возникают накладные расходы.
3

Если что-то является первичным ключом, в зависимости от вашего механизма БД вся таблица сортируется по первичному ключу. Это означает, что поиск по первичному ключу выполняется намного быстрее, потому что он не требует никакого разыменования, как это имеет место с любым другим типом индекса. Кроме того, это всего лишь теория.

Рэй Хидаят
источник
3
таблица будет отсортирована по кластеризованному индексу, а не по первичному ключу.
Ray Booysen
1
так уж получилось, что большинство людей устанавливают свой первичный ключ как кластерный индекс.
Ray Booysen
Что, как мы знаем, часто является действительно плохой идеей, если, конечно, нам не нравятся горячие точки и несбалансированные деревья индексов в наших таблицах ...
Майк Вудхаус
1
Это не ВСЕГДА действительно плохая идея. Знайте свои данные, свою РСУБД, знайте, что означает выбор. Редко выбор ВСЕГДА хороший или плохой. Если бы он был ВСЕГДА, база данных разрешила бы его или запретила. Они предоставляют вам выбор, потому что «это зависит от обстоятельств».
2

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

tddmonkey
источник
2

Пока вы не разрешаете NULL для значения, они должны обрабатываться одинаково, но значение NULL обрабатывается по-разному в базах данных (AFAIK MS-SQL не допускает более одного (1) значения NULL, mySQL и Oracle допускают это , если столбец UNIQUE) Таким образом, вы должны определить этот столбец NOT NULL UNIQUE INDEX

Питер Паркер
источник
1
MS-SQL допускает использование нескольких значений NULL в столбце с уникальным индексом, как и любая СУБД. Подумайте об этом так: NULL не является значением, поэтому, когда вы вставляете второй NULL, он никогда не будет соответствовать существующему. Выражение (NULL == NULL) не оценивается как истинное или ложное, оно оценивается как NULL.
gregmac
спасибо gregmac, я не был уверен, следует ли этому MS. Я вспомнил некоторые причуды MS с этим, однако несколько лет назад (до 2000 года), а также мог быть старый кашель
Питер Паркер
2

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

Уникальные индексы не являются частью стандарта SQL. Конкретная реализация СУБД будет определять, каковы последствия объявления уникального индекса.

В Oracle объявление первичного ключа приведет к созданию уникального индекса от вашего имени, поэтому вопрос почти спорный. Не могу рассказать о других продуктах СУБД.

Я предпочитаю объявить первичный ключ. Это приводит к запрету значений NULL в ключевом столбце (ах), а также к запрету дублирования. Я также предпочитаю объявлять ограничения REFERENCES для обеспечения целостности объекта. Во многих случаях объявление индекса в поле (ах) внешнего ключа ускоряет соединения. Такой индекс, как правило, не должен быть уникальным.

Уолтер Митти
источник
Первичный ключ в MS SQL Server всегда имеет значение UNIQUE и NOT NULL - например, это действительно просто уникальный индекс, но с добавленным ограничением, что он не может быть NULL.
marc_s
Oracle может применять ограничение уникальности с помощью неуникального индекса. Я был бы удивлен, если бы MSSS не смог. Сказать «это действительно просто уникальный индекс» - это медвежья услуга.
«Во многих случаях объявление индекса в поле (ах) внешнего ключа ускоряет соединения». это почти всегда неверно в мире хранилищ данных, где предпочтительнее хеш-соединения, если они доступны.
JAC2703 08
В ОП не упоминались склады. Я не уверен, как хеш-пояс работает на сервере sql. Сколько работы можно выполнить во время обновления склада.
Уолтер Митти
2

У КЛАСТЕРИРОВАННЫХ ИНДЕКСОВ по сравнению с УНИКАЛЬНЫМИ ИНДЕКСАМИ есть некоторые недостатки.

Как уже говорилось, КЛАСТЕРНЫЙ ИНДЕКС физически упорядочивает данные в таблице.

Это означает, что когда у вас есть много вставок или удалений в таблице, содержащей кластеризованный индекс, каждый раз (ну, почти, в зависимости от коэффициента заполнения) вы меняете данные, физическая таблица должна обновляться, чтобы оставаться отсортированной.

В относительно небольших таблицах это нормально, но когда вы переходите к таблицам с объемом данных в ГБ, а операции вставки / удаления влияют на сортировку, вы столкнетесь с проблемами.

Нико Бестер
источник
В чем же тогда преимущество? отсортированные запросы быстрее? это лучше для случая использования, когда вы пишете большую часть своих данных один раз (или редко) и запрашиваете их все время?
Buffalo
1

Я почти никогда не создаю таблицу без числового первичного ключа. Если есть еще естественный ключ, который должен быть уникальным, я также помещаю на него уникальный индекс. Соединения для целых чисел выполняются быстрее, чем для многоколоночных естественных ключей, данные должны изменяться только в одном месте (естественные ключи, как правило, необходимо обновлять, что плохо, когда они находятся в отношениях первичный ключ - внешний ключ). Если вам понадобится репликация, используйте GUID вместо целого числа, но по большей части я предпочитаю ключ, который читается пользователем, особенно если им нужно увидеть его, чтобы различать Джона Смита и Джона Смита.

Несколько раз я не создаю суррогатный ключ, когда у меня есть соединяющая таблица, которая участвует в отношении «многие ко многим». В этом случае я объявляю оба поля первичными ключами.

HLGEM
источник
«Я почти никогда не создаю таблицу без числового первичного ключа»: почему всегда числовой? Первичный ключ не обязательно должен быть числовым (кстати, не обязательно должен быть AUTO_INCREMENT).
Hibou57
@ Hinou57, потому что я обнаружил, что естественные ключи редко действительно уникальны и что они почти всегда можно изменить. Дальнейшие соединения по промежуточным числам обычно намного быстрее, чем соединения по естественным ключам varcahrr или, что еще хуже, составным ключам. Я бы не использовал их в большинстве случаев. Это может варьироваться в зависимости от типа информации, которую вы храните в своей базе данных, но по своему личному опыту я обнаружил, что естественные ключи со временем становятся крайне ненадежными.
HLGEM
Спасибо за ответ HLGEM. Что вы имеете в виду под ненадежным? Производительность? (Надеюсь, это не вопрос надежности в смысле целостности данных). Я немного удивлен вашими словами, поскольку я, хотя и использую целочисленные ключи или более естественные ключи, такие как короткий VARCHAR, скорее всего, имеет крошечное значение, поскольку хеширование используется везде, даже с самыми простыми механизмами БД.
Hibou57,
Они ненадежны во многих случаях, потому что не являются надежно уникальными, даже если они должны быть такими. Они ненадежны, поскольку изменяются и могут повлиять на миллионы записей в одном обновлении. Это мой опыт, когда я видел и управлял или запрашивал данные из сотен баз данных, в которых хранятся данные о многих различных типах информации, или импортировал данные из них.
HLGEM
1

Насколько я понимаю, первичный ключ и уникальный индекс с ненулевым ограничением - это одно и то же (*); и я предполагаю, что кто-то выберет тот или иной в зависимости от того, что явно указано или подразумевается в спецификации (вопрос того, что вы хотите выразить и явно обеспечить). Если для него требуется уникальность, а не значение NULL, сделайте его первичным ключом. Если так получилось, что все части уникального индекса не равны нулю без каких-либо требований, просто сделайте его уникальным индексом.

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

(*) За исключением практического различия: первичный ключ может быть уникальным ключом по умолчанию для некоторых операций, таких как определение внешнего ключа. Ex. если определить внешний ключ, ссылающийся на таблицу, и не указать имя столбца, если указанная таблица имеет первичный ключ, то первичный ключ будет столбцом, на который указывает ссылка. В противном случае столбец, на который указывает ссылка, должен быть назван явно.

Другие здесь упоминали репликацию БД, но я не знаю об этом.

Hibou57
источник
0

Уникальный индекс может иметь одно значение NULL. Создает НЕКЛАСТЕРНЫЙ ИНДЕКС. Первичный ключ не может содержать значение NULL. Создает КЛАСТЕРНЫЙ ИНДЕКС.

Чираг
источник
0

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

Markus
источник
-1

Если бы это было до меня ...

Вам необходимо удовлетворить требования базы данных и ваших приложений.

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

Затем вы должны добавить по крайней мере еще один уникальный индекс в таблицу для использования вашим приложением. Это может быть индекс для employee_id, account_id, customer_id и т. Д. Если возможно, этот индекс не должен быть составным индексом.

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

Я полностью сторонник использования вычисляемых индексов или индексов функционального типа и рекомендую использовать их вместо составных индексов. Это позволяет очень легко использовать индекс функции, используя ту же функцию в предложении where.

Это позаботится о ваших требованиях к приложению.

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

Родни П. Барбати
источник