MySQL: 查询有两个多对多关系和重复项,有双重中间 table
MySQL: query with two many to many relations and duplicates, with double intermediate table
这个问题是关于 select 在 MySQL 中跨多对多关系处理数据。与另外两个问题相关,但有一些区别:
这些问题使用了具有简单多对多关系的简单模型数据库:
article
article_author
author
article_tag
tag
现在我将介绍下一层的复杂性。我们希望每位作者都能够标记每篇 他们的 文章。因此,我们将 tags
连接到中间 table article_author
而不是直接连接到作者。
article
article_author
author
article_author_tag
tag
这里是 MySQL:
CREATE TABLE `article` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `author` (
`id` INT NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
);
CREATE TABLE `tag` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `article_author` (
`id` int NOT NULL AUTO_INCREMENT,
`author_id` INT NOT NULL,
`article_id` int NOT NULL,
`createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`author_id`,`article_id`),
KEY `fk_article_author_author1_idx` (`author_id`),
KEY `fk_article_author_article1_idx` (`article_id`),
CONSTRAINT `fk_article_author_article1` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`),
CONSTRAINT `fk_article_author_author1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`)
);
CREATE TABLE `article_author_tag` (
`article_author_id` int NOT NULL,
`tag_id` int NOT NULL,
PRIMARY KEY (`article_author_id`,`tag_id`),
KEY `fk_article_author_tag_article_author1_idx` (`article_author_id`),
KEY `fk_article_author_tag_tag1_idx` (`tag_id`),
CONSTRAINT `fk_article_author_tag_article_author1` FOREIGN KEY (`article_author_id`) REFERENCES `article_author` (`id`),
CONSTRAINT `fk_article_author_tag_tag1` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`)
);
INSERT INTO article (id, name) VALUES (1, 'first article'), (2, 'second article');
INSERT INTO `author` (id, name) VALUES (1, 'first author'), (2, 'second author');
INSERT INTO tag (id, name) VALUES (1, 'first tag'), (2, 'second tag');
INSERT INTO article_author (author_id, article_id) VALUES (1, 1), (2, 1);
INSERT INTO article_author_tag (article_author_id, tag_id) VALUES (1, 1), (1, 2), (2, 1), (2, 2);
现在,我只想 select 文章作者用来标记它的标签,作为 JSON 数组;但我无法摆脱重复:
SELECT
JSON_ARRAYAGG(tag.id)
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
它在数据库<>fiddle中:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=253f30ecd2f87b06c3894ef02b2ee35d
知道如何摆脱它们吗?
编辑:
我可以使用 CONCAT 和 GROUP_CONCAT,然后转换为 JSON。但它看起来很老套:
SELECT
CAST(CONCAT('[', GROUP_CONCAT(DISTINCT tag.id SEPARATOR ','), ']') AS JSON) AS tags
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
它在数据库<>fiddle中:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=20087a9036acb00637be8d2f58747ba5
欢迎任何其他想法!
json 还没有 distinct
功能(类似于 JSON_ARRAYAGG(distinct tag.id)
),但有一个通用的解决方法:
SELECT JSON_EXTRACT(JSON_OBJECTAGG(tag.id,tag.id),"$.*")
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
JSON_OBJECTAGG
作为隐含的不同,因为 json 标签根据定义是不同的,所以添加 {"1": 1}
两次结果只剩下一个。之后,您 JSON_EXTRACT
只需值即可获得您想要的格式(例如,没有人为添加的标签)。
另一种方法是向 json 函数提供已经正确、不同的数据:
SELECT JSON_ARRAYAGG(id)
FROM (
SELECT distinct tag.id
FROM article_author
JOIN article_author_tag
ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1
) subquery;
您首先按照您想要的方式准备数据(例如不同的标签 ID),然后使用 JSON_ARRAYAGG
格式化您的输出。
这个问题是关于 select 在 MySQL 中跨多对多关系处理数据。与另外两个问题相关,但有一些区别:
这些问题使用了具有简单多对多关系的简单模型数据库:
article
article_author
author
article_tag
tag
现在我将介绍下一层的复杂性。我们希望每位作者都能够标记每篇 他们的 文章。因此,我们将 tags
连接到中间 table article_author
而不是直接连接到作者。
article
article_author
author
article_author_tag
tag
这里是 MySQL:
CREATE TABLE `article` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `author` (
`id` INT NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
);
CREATE TABLE `tag` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `article_author` (
`id` int NOT NULL AUTO_INCREMENT,
`author_id` INT NOT NULL,
`article_id` int NOT NULL,
`createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`author_id`,`article_id`),
KEY `fk_article_author_author1_idx` (`author_id`),
KEY `fk_article_author_article1_idx` (`article_id`),
CONSTRAINT `fk_article_author_article1` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`),
CONSTRAINT `fk_article_author_author1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`)
);
CREATE TABLE `article_author_tag` (
`article_author_id` int NOT NULL,
`tag_id` int NOT NULL,
PRIMARY KEY (`article_author_id`,`tag_id`),
KEY `fk_article_author_tag_article_author1_idx` (`article_author_id`),
KEY `fk_article_author_tag_tag1_idx` (`tag_id`),
CONSTRAINT `fk_article_author_tag_article_author1` FOREIGN KEY (`article_author_id`) REFERENCES `article_author` (`id`),
CONSTRAINT `fk_article_author_tag_tag1` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`)
);
INSERT INTO article (id, name) VALUES (1, 'first article'), (2, 'second article');
INSERT INTO `author` (id, name) VALUES (1, 'first author'), (2, 'second author');
INSERT INTO tag (id, name) VALUES (1, 'first tag'), (2, 'second tag');
INSERT INTO article_author (author_id, article_id) VALUES (1, 1), (2, 1);
INSERT INTO article_author_tag (article_author_id, tag_id) VALUES (1, 1), (1, 2), (2, 1), (2, 2);
现在,我只想 select 文章作者用来标记它的标签,作为 JSON 数组;但我无法摆脱重复:
SELECT
JSON_ARRAYAGG(tag.id)
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
它在数据库<>fiddle中:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=253f30ecd2f87b06c3894ef02b2ee35d
知道如何摆脱它们吗?
编辑: 我可以使用 CONCAT 和 GROUP_CONCAT,然后转换为 JSON。但它看起来很老套:
SELECT
CAST(CONCAT('[', GROUP_CONCAT(DISTINCT tag.id SEPARATOR ','), ']') AS JSON) AS tags
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
它在数据库<>fiddle中:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=20087a9036acb00637be8d2f58747ba5
欢迎任何其他想法!
json 还没有 distinct
功能(类似于 JSON_ARRAYAGG(distinct tag.id)
),但有一个通用的解决方法:
SELECT JSON_EXTRACT(JSON_OBJECTAGG(tag.id,tag.id),"$.*")
FROM article_author
JOIN article_author_tag ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1;
JSON_OBJECTAGG
作为隐含的不同,因为 json 标签根据定义是不同的,所以添加 {"1": 1}
两次结果只剩下一个。之后,您 JSON_EXTRACT
只需值即可获得您想要的格式(例如,没有人为添加的标签)。
另一种方法是向 json 函数提供已经正确、不同的数据:
SELECT JSON_ARRAYAGG(id)
FROM (
SELECT distinct tag.id
FROM article_author
JOIN article_author_tag
ON article_author_tag.article_author_id = article_author.id
JOIN tag ON article_author_tag.tag_id = tag.id
WHERE article_author.article_id = 1
) subquery;
您首先按照您想要的方式准备数据(例如不同的标签 ID),然后使用 JSON_ARRAYAGG
格式化您的输出。