Этот вопрос касается выбора данных по отношениям «многие ко многим» в MySQL. Относится к другим двум вопросам, но с некоторыми отличиями:
В этих вопросах использовался простой макет базы данных с простыми отношениями «многие ко многим»:
article
article_author
author
article_tag
tag
Сейчас я представлю следующий уровень сложности. Мы хотим, чтобы каждый автор мог пометить каждую из своих статей. Таким образом, мы подключим tags
к промежуточной таблице 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);
И теперь я хотите просто выбрать теги, которые авторы статьи использовали для маркировки, как массив 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;
Вот он в скрипте db <>: 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;
Вот он в скрипте db <>: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=20087a9036acb00637be8d2f58747ba5
Любая другая идея будет приветствоваться!