JOINing多对多表时避免SQL查询中的重复行
Posted
技术标签:
【中文标题】JOINing多对多表时避免SQL查询中的重复行【英文标题】:Avoid duplicated rows in SQL query when JOINing many to many tables 【发布时间】:2021-02-08 23:19:24 【问题描述】:我有一个基本的博客系统,其中包含帖子、作者和标签的表格。
一个作者可以写一篇文章,但一篇文章只能由一个作者写(一对多关系)。一个标签可以出现在许多不同的帖子中,任何帖子都可以有多个标签(多对多关系)。在这种情况下,我创建了第 4 个表来链接帖子和标签,如下所示:
post_id -> posts_tag
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 4 | 1 |
我需要一个查询来列出每个帖子及其用户及其标签(如果有的话)。我非常接近双重 JOIN 查询,但是对于具有多个标签的帖子,我得到了重复的行(该行中的所有内容都是重复的,但标签寄存器)。我使用的查询如下:
SELECT title,
table_users.username author,
table_tags.tagname tag
FROM table_posts
JOIN table_users
ON table_posts.user_id = table_users.id
LEFT
JOIN table_posts_tags
ON table_posts.id = table_posts_tags.post_id
LEFT
JOIN table_tags
ON table_tags.id = table_posts_tags.tag_id
当有多个标签与同一个帖子关联时,任何人都可以建议修改此查询或新的适当的查询来解决行重复问题*吗?类型
(*) 为了清楚起见:在上表中,查询将抛出 4 行,当它应该抛出 3 时,第 1 条帖子(带有 2 个标签)有 1 行,第 2 条帖子有 1 行,帖子有 1 行#4.
重新创建表
CREATE TABLE `table_posts` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(120) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`),
)
CREATE TABLE `table_tags` (
`id` int NOT NULL AUTO_INCREMENT,
`name_tag` varchar(18) NOT NULL,
PRIMARY KEY (`id`)
)
CREATE TABLE `table_posts_tags` (
`id` int NOT NULL AUTO_INCREMENT,
`post_id` int NOT NULL,
`tag_id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `tag_id` (`tag_id`),
KEY `FK_t_posts_tags_t_posts` (`post_id`),
CONSTRAINT `FK_t_posts_tags_t_posts` FOREIGN KEY (`post_id`) REFERENCES `t_posts` (`id`),
CONSTRAINT `FK_t_posts_tags_t_tags` FOREIGN KEY (`tag_id`) REFERENCES `t_tags` (`id`)
)
CREATE TABLE `table_users` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(16) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`banned` tinyint DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_t_users_t_roles` (`role_id`),
CONSTRAINT `FK_t_users_t_roles` FOREIGN KEY (`role_id`) REFERENCES `t_roles` (`id`)
)
【问题讨论】:
欢迎来到 SO。请看:Why should I provide an MCRE for what seems to me to be a very simple SQL query? GROUP By 或在加入之前减少行数,这取决于您的结果是什么 嗨草莓。明天我会尝试从学校提供 CREATE 查询。泰! 添加表创建 【参考方案1】:一个选项使用group by
和group_concat()
将标签聚合到一个CSV 列表中:
select p.title, u.username author, group_concat(t.tagname) tagnames
from table_posts p
inner join table_users u on u.id = p.user_id
left join table_posts_tags pt on pt.post_id = p.id
left join table_tags t on t.id = tp.tag_id
group by p.id, p.title, u.username
请注意,我在查询中添加了表别名,并使用它们来限定所有列;这使得查询更短,更易于编写和阅读。
【讨论】:
Ty GMB 但您的解决方案没有保留原始表结构,按照 SQL 理论,“多对多”关系应该依赖于 2 个主表和一个连接。我们已经考虑过 CSV 解决方案,但似乎没有遵循理想的数据架构。如果我错了,或者根本不可能(出于某种理论上的原因)用我们的实际表格设计来解决它,请纠正我。是的,我们在学校! :-) @hip: 那么当一个帖子有三个标签时,你想要哪个结果? Result 应该是按发布顺序排列的帖子列表(相当于 ID 顺序)。列表中的每个项目如下:帖子标题/作者姓名/标签列表(例如,由空格分隔) @hip:好吧,这就是查询的作用。以逗号分隔的标签列表,但可以轻松更改。 空格、逗号、破折号......任何都可以,这不是问题以上是关于JOINing多对多表时避免SQL查询中的重复行的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 基础 -- 多表关系(一对一1对多(多对一)多对多)多表查询(内连接外连接自连接子查询(嵌套查询)联合查询 union)笛卡儿积