SQL - первичный ключ таблицы многие ко многим

125

Этот вопрос возникает после прочтения комментария к этому вопросу:

Дизайн базы данных

Когда вы создаете таблицу «многие ко многим», следует ли вам создать составной первичный ключ для двух столбцов внешнего ключа или создать суррогатный первичный ключ «ID» с автоинкрементом и просто поместить индексы в два столбца FK (и, возможно, уникальное ограничение)? Каково влияние на производительность вставки новых записей / повторной индексации в каждом случае?

В основном это:

PartDevice
----------
PartID (PK/FK)
DeviceID (PK/FK)

против этого:

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)

Комментатор говорит:

превращение двух идентификаторов в PK означает, что таблица физически сортируется на диске в указанном порядке. Итак, если мы вставим (Part1 / Device1), (Part1 / Device2), (Part2 / Device3), тогда (Part 1 / Device3) базе данных придется разбить таблицу на части и вставить последнюю между записями 2 и 3. Для При большом количестве записей это становится очень проблематичным, поскольку требует перетасовки сотен, тысяч или миллионов записей при каждом добавлении одной. Напротив, автоинкрементный PK позволяет прикреплять новые записи до конца.

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

Энди Уайт
источник
Вот силимарный вопрос, размещенный на SO: stackoverflow.com/questions/344068/…
Тони
(Пытался добавить это в свой предыдущий комментарий, но не смог). В зависимости от количества вставок вы также можете периодически перестраивать свой индекс, чтобы он быстро возвращал результаты. В SQL Server вы также можете настроить FILLFACTOR индекса, чтобы предоставить достаточно места для вставок, прежде чем он должен будет перемещать данные.
Тони
1
Разве ответ на этот вопрос не зависит от того, какая СУБД используется? Я подозреваю, что в этом случае MySQL будет вести себя определенным образом, SQL-сервер - немного иначе и т. Д.
Раду Мурзеа,
Предостережение: без конкретного тега базы данных многое из того, что здесь говорится, является подозрительным. Разные двигатели работают по разному!
Рик Джеймс

Ответы:

85

При простом отображении «многие ко многим» из двух столбцов я не вижу реальных преимуществ в наличии суррогатного ключа. Имея первичный ключ на (col1,col2)гарантированно уникальном (предполагается , что col1и col2значение в справочных таблицах являются уникальным) и отдельным индексом на (col2,col1)поймаете те случаи , когда порядок напротив будет выполнять быстрее. Суррогат - пустая трата места.

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

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

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

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

paxdiablo
источник
Последний пункт не является хорошим обобщением: «подавляющее большинство таблиц базы данных читаются гораздо чаще, чем записываются». Я нахожу много примеров ассоциативных таблиц, в которые нужно писать очень часто, например, таблица, связывающая клиента с заказом.
пользователь
5
@buffer, я поддержу этот комментарий (технически это обобщение, только если я скажу «все таблицы», «подавляющее большинство» основано на опыте). Давайте также подумаем о вашем примере, заказ создается один раз (он может обновляться время от времени, но это вряд ли изменит информацию о ключе / индексе, в большей степени, чтобы затронуть такие вещи, как статус заказа. распечатка счетов-фактур или генерация управленческих отчетов будут перевешивать исходную вставку.
paxdiablo
Подумайте об Amazon - каждый час создаются тысячи заказов.
пользователь
9
@buffer, да, но опять же, каждый из этих заказов почти наверняка будет запрашиваться много раз, например, для упаковки, выставления счетов, обновления статуса, бизнес-аналитики и так далее. Абсолютное количество созданий менее важно, чем соотношение между созданными и прочитанными.
paxdiablo
1
Я insertхочу сказать , будет ли это иметь значение, если это будет выполняться тысячи раз в час. Вы не можете просто игнорировать его только потому, что отношение insertк select<1. В этом случае покупатель заботится о том, сколько времени уходит на размещение заказа.
пользователь
19

Для таблиц ссылок суррогатный ключ не требуется.

Один ПК на (col1, col2) и еще один уникальный индекс на (col2, col1) - это все, что вам нужно

Если вы не используете ORM, который не справляется и не диктует вам дизайн вашей БД ...

Изменить: я ответил то же самое здесь: SQL: вам нужен автоинкрементный первичный ключ для многих-многих таблиц?

ГБН
источник
3
Возможно, вы будете в порядке с индексом дублирования на col2 вместо уникального индекса на (col2, col1). Преимущество индекса с двумя столбцами состоит в том, что он позволяет сканировать только по индексу либо по одному столбцу col2, либо по столбцам col1 и col2 (хотя другой индекс, on (col1, col2), также обрабатывает «оба» случая). Обратной стороной является дополнительное пространство, необходимое для дополнительной колонки. Обычно это не имеет значения, поэтому совет далеко не ужасен. Тем не менее, если столбцы col1 и col2 большие или очень разных размеров, вы можете сэкономить место без ущерба для производительности, выбрав второй индекс только для более короткого столбца.
Джонатан Леффлер
@gbn: второй индекс в (col2, col1) не обязательно должен быть уникальным, верно?
пользователь
1
установка уникального индекса на (col1, col2) после того, как он уже является PK, полностью избыточна
Дон Чидл
@mmcrae: где мы это делаем?
gbn 05
2
@mmcrae: Ваш комментарий "устанавливает уникальный индекс для (col1, col2) ..". Порядок столбцов в индексе имеет значение. (col2, col1)нет (col1, col2). PK of (col1, col2)может не подходить для всех запросов и генерировать сканирование, поэтому использование обратного действия улучшает производительность, поскольку позволяет искать там, где col2 лучше. Например, проверка FK, когда таблица с col2 имеет удаление. Загрязнение дочерней таблицы должно быть проверено
gbn 07
12

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

например

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)
Other Details

«Прочие детали» легко получить, используя PartDevice.ID в качестве FK. Таким образом, необходимо использовать инкрементный первичный ключ.

Jronny
источник
1
Спасибо! Я пришел к ответу, так как искал почти тот же сценарий, который вы описали. Но вы отошли от своего первого предложения, добавив «Прочие детали». Что, если бы у меня была таблица сопоставления "многие ко многим", на которую мне нужно ссылаться из другой таблицы? Это означает, что таблица сопоставления многие ко многим не хранит никакой другой информации ... Будет ли вообще иметь смысл дополнительный столбец идентификатора? Если нет, то как вместо этого ссылаться на одну запись таблицы сопоставления?
мизантроп
Здесь есть два варианта: вы можете использовать составной ключ в качестве внешнего ключа из вашей ссылающейся таблицы (это добавляет дополнительный столбец в вашу новую таблицу) или вы можете создать столбец идентификатора в таблице сопоставления и установить уникальное ограничение для исходного соединения первичный ключ, а новый столбец идентификатора станет первичным ключом.
Vočko
6

Самый короткий и прямой способ ответить на ваш вопрос - это сказать, что производительность будет снижаться, если две таблицы, которые вы связываете, не имеют последовательных первичных ключей. Как вы заявили / цитировали, индекс для таблицы ссылок либо станет фрагментированным, либо СУБД будет труднее вставлять записи, если таблица ссылок не имеет своего собственного последовательного первичного ключа. По этой причине большинство людей помещают последовательно увеличивающийся первичный ключ в таблицы ссылок.

Бернхард Хофманн
источник
2

Таким образом, похоже, что если ЕДИНСТВЕННАЯ задача - связать две таблицы, лучшим ПК будет ПК с двумя столбцами.

Но если он служит другим целям, добавьте еще один NDX в качестве PK с внешними ключами и вторым уникальным индексом.

Индекс или PK - лучший способ убедиться в отсутствии дубликатов. PK позволяет таким инструментам, как Microsoft Management Studio, выполнять часть работы (создавать представления) за вас.

Михаил Косак
источник