Votre seconde requête est de la forme :
q1 -- PK user_id
LEFT JOIN (...
GROUP BY user_id, t.tag
) AS q2
ON q2.user_id = q1.user_id
LEFT JOIN (...
GROUP BY user_id, c.category
) AS q3
ON q3.user_id = q1.user_id
GROUP BY -- group_concats
Les GROUP BY internes donnent (user_id, t.tag)
&(user_id, c.category)
étant des clés/UNIQUES. En dehors de cela, je n'aborderai pas ces GROUP BY.
TL;DR Lorsque vous joignez (q1 JOIN q2) à q3, ce n'est pas sur une clé/UNIQUE de l'un d'entre eux, donc pour chaque user_id, vous obtenez une ligne pour chaque combinaison possible de balise et de catégorie. Ainsi, les entrées GROUP BY finales sont dupliquées par (user_id, tag) et par (user_id, catégorie) et GROUP_CONCATs duplique de manière inappropriée les balises et les catégories par user_id. Correct serait (q1 JOIN q2 GROUP BY) JOIN (q1 JOIN q3 GROUP BY) dans lequel toutes les jointures sont sur la clé commune/UNIQUE (user_id)
&il n'y a pas de fausse agrégation. Bien que vous puissiez parfois annuler une telle agrégation fallacieuse.
Une approche INNER JOIN symétrique correcte :LEFT JOIN q1 &q2--1:many--then GROUP BY &GROUP_CONCAT (ce que votre première requête a fait) ; puis séparément de manière similaire LEFT JOIN q1 &q3--1:many--then GROUP BY &GROUP_CONCAT; puis INNER JOIN les deux résultats ON user_id--1:1.
Une approche de sous-requête scalaire symétrique correcte :SÉLECTIONNEZ les GROUP_CONCAT de q1 en tant que sous-requêtes scalaires chacun avec un GROUP BY.
Une approche LEFT JOIN cumulative correcte :LEFT JOIN q1 &q2--1:many--then GROUP BY &GROUP_CONCAT ; puis LEFT JOIN that &q3--1:many--puis GROUP BY &GROUP_CONCAT.
Une approche correcte comme votre 2ème requête :vous avez d'abord LEFT JOIN q1 &q2--1:many. Ensuite, vous avez quitté JOIN that &q3--many:1:many. Il donne une ligne pour chaque combinaison possible d'une balise et d'une catégorie qui apparaissent avec un user_id. Ensuite, après avoir GROUP BY, vous GROUP_CONCAT - sur les paires en double (user_id, tag) et les paires en double (user_id, catégorie). C'est pourquoi vous avez des éléments de liste en double. Mais ajouter DISTINCT à GROUP_CONCAT donne un résultat correct. (Par wchiquito commentaire de.)
Ce que vous préférez est comme d'habitude un compromis d'ingénierie pour être informé par les plans et les délais de requête, par données/utilisation/statistiques réelles. entrée et statistiques pour la quantité de duplication attendue), le moment des requêtes réelles, etc. Un problème est de savoir si les lignes supplémentaires de l'approche JOIN many:1:many compensent sa sauvegarde d'un GROUP BY.
-- cumulative LEFT JOIN approach
SELECT
q1.user_id, q1.user_name, q1.score, q1.reputation,
top_two_tags,
substring_index(group_concat(q3.category ORDER BY q3.category_reputation DESC SEPARATOR ','), ',', 2) AS category
FROM
-- your 1st query (less ORDER BY) AS q1
(SELECT
q1.user_id, q1.user_name, q1.score, q1.reputation,
substring_index(group_concat(q2.tag ORDER BY q2.tag_reputation DESC SEPARATOR ','), ',', 2) AS top_two_tags
FROM
(SELECT
u.id AS user_Id,
u.user_name,
coalesce(sum(r.score), 0) as score,
coalesce(sum(r.reputation), 0) as reputation
FROM
users u
LEFT JOIN reputations r
ON r.user_id = u.id
AND r.date_time > 1500584821 /* unix_timestamp(DATE_SUB(now(), INTERVAL 1 WEEK)) */
GROUP BY
u.id, u.user_name
) AS q1
LEFT JOIN
(
SELECT
r.user_id AS user_id, t.tag, sum(r.reputation) AS tag_reputation
FROM
reputations r
JOIN post_tag pt ON pt.post_id = r.post_id
JOIN tags t ON t.id = pt.tag_id
WHERE
r.date_time > 1500584821 /* unix_timestamp(DATE_SUB(now(), INTERVAL 1 WEEK)) */
GROUP BY
user_id, t.tag
) AS q2
ON q2.user_id = q1.user_id
GROUP BY
q1.user_id, q1.user_name, q1.score, q1.reputation
) AS q1
-- finish like your 2nd query
LEFT JOIN
(
SELECT
r.user_id AS user_id, c.category, sum(r.reputation) AS category_reputation
FROM
reputations r
JOIN post_category ct ON ct.post_id = r.post_id
JOIN categories c ON c.id = ct.category_id
WHERE
r.date_time > 1500584821 /* unix_timestamp(DATE_SUB(now(), INTERVAL 1 WEEK)) */
GROUP BY
user_id, c.category
) AS q3
ON q3.user_id = q1.user_id
GROUP BY
q1.user_id, q1.user_name, q1.score, q1.reputation
ORDER BY
q1.reputation DESC, q1.score DESC ;