Области применения / составные суррогатные ключи в MySQL - PullRequest
0 голосов
/ 11 апреля 2009

Вот выдержка из моей текущей базы данных (для облегчения понимания изменил имена таблиц):

Pet(ownerFK, id, name, age)
Owner(id, name)

Где id - это всегда суррогатный ключ, созданный с помощью auto_increment.

Я хочу, чтобы суррогатный ключ Pet.id был "ограничен" Pet.ownerFK или, другими словами, в качестве минимального ключа был бы составной ключ [ownerFk, id]. Я хочу, чтобы таблица вела себя так:

INSERT Pet(1, ?, "Garfield", 8);
INSERT Pet(1, ?, "Pluto", 12);
INSERT Pet(2, ?, "Mortimer", 1);

SELECT * FROM Pet;
  RESULT:
   Pet(1, 1, "Garfield", 8)
   Pet(1, 2, "Pluto", 12)
   Pet(2, 1, "Mortimer", 1)

В настоящее время я использую эту функцию MyISAM , где «вы можете указать AUTO_INCREMENT для дополнительного столбца в индексе из нескольких столбцов. В этом случае сгенерированное значение для столбец AUTO_INCREMENT рассчитывается как MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. Это полезно, когда вы хотите поместить данные в упорядоченные группы. "

Однако из-за различных (и, возможно, очевидных) причин я хочу переключиться с MyISAM на InnoDB, так как мне нужны транзакции в некоторых местах.

Есть ли способ, как добиться этого эффекта с помощью InnoDB ?

Я нашел несколько сообщений по этому вопросу, многие из них предложили заблокировать запись таблицы перед вставкой. Я не очень знаком с этим, но не будет ли блокировка таблицы записи немного капитальным ремонтом для этого? Я скорее думал о том, чтобы иметь безопасные от записи транзакции (чего я никогда раньше не делал), если это возможно - иметь Owner.current_pet_counter в качестве вспомогательного поля.

Таким образом, другим приемлемым решением будет ...

На самом деле мне не нужен идентификатор "scoped", чтобы быть частью настоящего ключа. Мой фактический дизайн базы данных использует отдельную таблицу «постоянных ссылок», которая использует эту «функцию». В настоящее время я использую его в качестве обходного пути для отсутствующих транзакций. Я подумал о следующей альтернативе:

 Pet(id, ownerFK, scopedId, name, age), KEY(id), UNIQUE(ownerFK, scopedId)
 Owner(id, name, current_pet_counter)

 START TRANSACTION WITH CONSISTENT SNAPSHOT;
 SELECT @new=current_pet_counter FROM Owner WHERE id = :owner_id;
 INSERT Pet(?, :owner_id, @new, "Pluto", 21);
 UPDATE Owners SET current_pet_counter = @new + 1 WHERE id = :owner_id;
 COMMIT;

Я еще не работал с транзакциями / транзакциями в MySQL, поэтому я не знаю, будут ли серьезные проблемы с этим. Примечание: Я не хочу повторно использовать id s, которые были даны домашнему животному один раз. Вот почему я не использую MAX(). Есть ли у этого решения какие-либо предостережения?

1 Ответ

1 голос
/ 11 апреля 2009

Я не верю в это. Если вам действительно нужна эта схема, вы можете использовать транзакцию для ВЫБОР МАКСИМАЛЬНОГО (id) ГДЕ ownerFK, а затем ВСТАВИТЬ.

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

Как правило, вы бы хотели, чтобы «id» действительно был правильным первичным ключом сам по себе, для того, чтобы ownerFK использовал для группировки и, если вам это нужно, отдельный столбец «rank», чтобы размещать домашних животных в определенном порядке для каждого владельца, и УНИКАЛЬНЫЙ индекс над (ownerFK, rank).

...