我正在为私人网站开发CMS系统(主要是作为学习练习)。 Atm我有三个表:一个用于文章,一个用于标签和一个连接表,以便每篇文章可以有多个标签。
我遇到问题的表格包含三列 -
article_tags: id (auto_increment), article_id, tag_id
我的问题源于这样一个事实:一篇文章可以出现任意次,而且一个标签也可以出现任意次,但是两者的给定组合应该只出现一次 - 也就是说,每篇文章应该只有对任何单个标签的一个引用。目前可以插入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来做)。由于id不同而且无法设置唯一列,例如INSERT IGNORE和ON DUPLICATE不起作用。
我对mySQL很陌生,所以如果我做些傻事,请指出我正确的方向。
由于
答案 0 :(得分:3)
这种多对多关系表(有时称为连接表)通常只有两列,并且主键是两者的组合。
article_id
tag_id
pk = (article_id, tag_id)
如果您更改该表的定义,您将明确解决该问题。
如何在复合键中订购列?这取决于您的应用程序如何在连接表中查找项目。如果你总是从article_id开始并查找tag_id,那么你首先将article_id放在键中。 DBMS可以随机访问密钥中第一列的值,但必须扫描索引以在密钥的第二列(或后续列)中查找值。
您可能希望在表(tag_id, article_id)
上创建第二个索引。这将允许基于tag_id的快速查找。你可能会问,“为什么还要把两列放在索引中呢?”这是为了使索引成为覆盖索引。在覆盖索引中,可以直接从索引中检索所需的值。例如,使用覆盖索引
SELECT article_id FROM article_tag WHERE tag_id = 12345
(或使用类似查找逻辑的JOIN)只需访问磁盘驱动器上的索引即可获得结果。如果您没有覆盖索引,则查询需要从索引跳转到数据表,这是一个额外的步骤。
连接表通常具有非常短的行(几个整数),因此几个覆盖索引(主键和额外索引)的重复数据不是一个大的磁盘空间。
答案 1 :(得分:3)
您应该查看表格定义。
你可以(从最佳到最差):
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;
无论如何,如果您不想更改表定义,可以在php查询中始终使用DISTINCT:
SELECT DISTINCT(article_id, tag_id) FROM article_tags