#mysql #sql
#mysql #sql
Вопрос:
У меня есть таблица «статьи», и у меня есть таблица «теги». Между ними существует отношение N: N, поэтому у меня также есть таблица ‘article_tags_relations’. Я хотел бы получить список статей с тегами, однако в дополнение к имени тега мне также нужен его идентификатор. Таким образом, возвращаемые данные должны быть чем-то вроде (конечно, не в формате JSON):
[[1,"FirstArticle",{"1":"FirstTag","2":"SecondTag"}],[2,"SecondArticle",{"3":"ThirdTag"}]]
Пока у меня есть что-то вроде:
SELECT article.*, GROUP_CONCAT(tag.name SEPARATOR ', ') AS tags FROM articles AS article LEFT JOIN articles_tags_relations AS relation ON article.id = relation.article_id LEFT JOIN tags AS tag ON relation.tag_id = tag.id LIMIT 0,10;
Но в нем есть только имена тегов без идентификаторов. В таблице будет много запросов на чтение, поэтому важна производительность. Я не уверен, что использование GROUP_CONCAT — это правильный путь, я открыт для любых идей. Спасибо!
Ответ №1:
Обязательно ли делать это в одном запросе? Я бы выполнил один запрос, чтобы получить статьи, а затем второй, чтобы получить теги для этих идентификаторов статей. Хотя я не уверен, как сравнивается производительность. (И это, вероятно, зависит от скорости вашей сети относительно стоимости второго запроса по сравнению с левым соединением.)
Тем не менее, попытка объединить несколько фрагментов данных в sql-запросе, похоже, противоречит духу реляционной базы данных и с меньшей вероятностью будет выполняться так же хорошо, как то, в чем база данных действительно хороша.
Комментарии:
1. Нет, не важно делать это в одном запросе. Итак, вы говорите, что я должен сделать что-то вроде следующего? ВЫБЕРИТЕ статью.* ИЗ статей; ВЫБЕРИТЕ тег.* ИЗ article_tags_relations В качестве отношения СЛЕВА ПРИСОЕДИНИТЕ теги КАК тег К отношению.tag_id = tag.id ГДЕ relation.article_id В (1,2,3,4,5, …);
2. Обычно я бы сделал то, что предложил Марсель (за исключением внешнего соединения). В вашем случае я понимаю, что этого делать нельзя, поскольку вы не хотите отправлять текст статьи несколько раз из-за производительности / пропускной способности. Итак, да, я хотел бы выполнить два запроса, которые вы описали. Почти. Вам все еще нужен article_id во втором запросе, чтобы вы знали, какой тег идет с какой статьей. Цель состоит в том, чтобы извлечь текст статьи из объединенного запроса.
Ответ №2:
Насколько я знаю, GROUP_CONCAT()
работает вместе с GROUB BY
предложением. Однако, вот две альтернативы получения tag.id вашими данными:
SELECT article.*, tag.id AS tags_id, GROUP_CONCAT(tag.name SEPARATOR ', ') AS tags FROM articles AS article LEFT JOIN articles_tags_relations AS relation ON article.id = relation.article_id LEFT JOIN tags AS tag ON relation.tag_id = tag.id LIMIT 0,10;
И 2:
SELECT article.*, GROUP_CONCAT(tag.name SEPARATOR ', ') AS tags, GROUP_CONCAT(tag.id SEPARATOR ', ') AS tag_ids FROM articles AS article LEFT JOIN articles_tags_relations AS relation ON article.id = relation.article_id LEFT JOIN tags AS tag ON relation.tag_id = tag.id LIMIT 0,10;
Ответ №3:
Вы почти на месте. Добавьте, CONCAT
чтобы получить идентификатор и запись вместе, или создайте отдельное GROUP_CONCAT
предложение для каждого.
SELECT article.*,
GROUP_CONCAT(CONCAT(tag.id,':',tag.name)) AS tags
FROM articles AS article
LEFT JOIN articles_tags_relations AS relation ON article.id = relation.article_id
LEFT JOIN tags AS tag ON relation.tag_id = tag.id
GROUP BY article.id
LIMIT 0,10
AFAIK нет ничего особенного в том, GROUP_CONCAT
что приведет к потере производительности.
Просто для развлечения вот запрос, который вернет JSON
SELECT CONCAT('[',GROUP_CONCAT(CONCAT('[',row_data,']')),']') as JSON
FROM
(
SELECT CONCAT(a.id,',"',a.title,'",{',GROUP_CONCAT(CONCAT("'",tag.id,'":"',tag.name,'"')),'}') as row_data
FROM articles AS a
LEFT JOIN articles_tags_relations AS relation ON a.id = relation.article_id
LEFT JOIN tags AS tag ON relation.tag_id = tag.id
GROUP BY a.id
LIMIT 0,10
) t1
Ответ №4:
Извините, я, кажется, не понимаю вашей проблемы. Для меня решения просты: select a.id, a.name, t.id, t.name from articles a, tags t, articles_tags_relations atr where a.id = atr.article_id and t.id = atr.tag_id
Конечно, это не форматирует результат так, как вы хотите, но форматирование — это не то, для чего предназначен SQL. Сначала вы получаете результат (см. Выше), затем вы используете язык программирования по вашему выбору для форматирования результирующего набора.
Комментарии:
1. Статья может иметь более одного тега. В вашем SQL-запросе извлекается только один тег.
2. Нет, конечно, нет. Результирующий набор будет выглядеть как «1 article1 1 tag1 / 1 article1 2 tag2 / 1 article1 3 tag3» и так далее. Я упоминал, что он не будет хорошо отформатирован.