2014-01-25 6 views
0

Я работаю над системой CMS (в основном как учебное упражнение) для частного веб-сайта. У меня есть три таблицы: одна для статей, одна для тегов и соединительная таблица, так что каждая статья может иметь несколько тегов.Избегайте дублирования записей в таблице mySQL с уникальными столбцами

В таблице у меня возникли проблемы с состоит из трех колонок -

article_tags: id (auto_increment), article_id, tag_id 

Моя проблема связана с тем, что статья может появиться любое количество раз, и тег может также появиться любое количество раз, однако данная комбинация из них должна появляться только один раз - то есть каждая статья должна иметь только одну ссылку на любой отдельный тег. В настоящее время можно ВСТАВИТЬ «дублировать» строки, где идентификатор отличается, но сочетание article_id и tag_id одинаковы:

id , article_id, tag_id 
1  1   1 
2  1   2  
3  2   1  
4  1   1 <- this is wrong 

я мог проверить в PHP код для записи, содержащей эту комбинацию, но я Предпочитаете сделать это в sql, если это возможно (если это не так, или это нежелательно, то я сделаю это с помощью PHP). Из-за того, что идентификатор отличается и невозможность установить уникальные столбцы, такие как INSERT IGNORE и ON DUPLICATE не работают.

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

Благодаря

ответ

3

Вы должны просмотреть определение своей таблицы.

Вы можете (от лучшего к худшему):

  1. Добавить составной первичный ключ (article_id и tag_id) и удалить auto_increment (предыдущий первичный ключ)
  2. Добавить индекс (UNIQUE) на (article_id и tag_id) и держать ваш auto_increment первичный ключ
  3. Выберите отчетливый в PHP: SELECT DISTINCT(article_id, tag_id) FROM ... ничего не меняя в своей таблице

Прямо сейчас, ваша таблица определяется как что-то вроде этого:

CREATE TABLE IF NOT EXISTS `article_tags` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `article_id` int(11) NOT NULL, 
    `tag_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Лучшее решение (вариант 1) будет удалить текущий (auto_increment) первичный ключ и добавить первичный ключ (композитный) на колонках article_id и tag_id:

CREATE TABLE IF NOT EXISTS `article_tags` (
    `article_id` int(11) NOT NULL, 
    `tag_id` int(11) NOT NULL, 
    PRIMARY KEY (`article_id`,`tag_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Но (вариант 2), если вы абсолютно хотите сохранить auto_increment первичного ключа, добавьте индекс (уникальный) на столбцах:

CREATE TABLE IF NOT EXISTS `article_tags` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `article_id` int(11) NOT NULL, 
    `tag_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `article_id` (`article_id`,`tag_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Во всяком случае, если вы не хотите, чтобы изменить таблицу Definitio n, вы всегда можете использовать DISTINCT в своем запросе php:

SELECT DISTINCT(article_id, tag_id) FROM article_tags 
+0

Очень краткий ответ. Если я правильно понимаю, что №3 хуже, потому что он добавляет служебные данные в запрос SELECT? И хуже # 2, потому что он по сути делает то же самое, что и # 1, но имеет дополнительный (ненужный?) Столбец в виде старого auto_increment? – ProFishChris

+0

Не лаконично. См. Мой отредактированный ответ –

+0

Более подробно, хотя! Извините за путаницу, но меня больше интересовало, почему вы заказали их так, как вы это делали, а не как их реализовать. – ProFishChris

3

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

article_id 
    tag_id 
    pk = (article_id, tag_id) 

Если вы измените определение этой таблицы, вы решительно решите эту проблему.

Как заказать столбцы в составных ключах? Это зависит от того, как ваше приложение будет искать элементы в таблице соединений. Если вы всегда начинаете с article_id и просматриваете tag_id, тогда вы кладете article_id сначала в ключ. СУБД может осуществлять произвольный доступ к значениям для первого столбца в ключе, но при этом необходимо сканировать индекс, чтобы находить значения во втором (или последующем) столбцах в ключе.

Возможно, вы захотите создать второй индекс на столе, (tag_id, article_id). Это позволит быстро искать на основе tag_id. Вы можете спросить: «Зачем ставить обе колонки в индекс?» То есть, чтобы индекс стал индексом покрытия . В индексе покрытия желаемое значение может быть получено непосредственно из индекса. Так, например, с индексом накрывающей,

SELECT article_id FROM article_tag WHERE tag_id = 12345 

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

Столбцы с объединением обычно имеют очень короткие строки (пару целых чисел), поэтому дублированные данные для нескольких индексов покрытия (первичный ключ и дополнительный) не являются большим ботком на диске.

+0

это именно то, что я искал. Композитные клавиши - это не то, с чем я столкнулся в моем, по общему признанию, ограниченном опыте. Есть ли заметная разница между порядком составного индекса или не имеет значения? – ProFishChris

+0

@ProFishChris, см. Мое редактирование. Хороший вопрос. –

+0

Ничего себе, это было намного больше, чем я думал. Очень интересно. Спасибо за разъяснение – ProFishChris

Смежные вопросы