sql >> Database teknologi >  >> RDS >> Mysql

Mærkelig dubletadfærd fra GROUP_CONCAT af to LEFT JOINs af GROUP_BYs

Din anden forespørgsel er af formen:

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
 

De indre GROUP BY'er resulterer i (user_id, t.tag) &(user_id, c.category) være nøgler/UNIKKE. Udover det vil jeg ikke tage fat på disse GROUP BY'er.

TL;DR Når du tilslutter (q1 JOIN q2) til q3 er det ikke på en nøgle/UNIQUE af en af ​​dem, så for hvert user_id får du en række for hver mulig kombination af tag &kategori. Så den endelige GROUP BY indlæser dubletter pr. (user_id, tag) &per (user_id, category) og upassende GROUP_CONCATs duplikater af tags og kategorier pr. bruger_id. Korrekt ville være (q1 JOIN q2 GROUP BY) JOIN (q1 JOIN q3 GROUP BY), hvor alle joins er på fælles nøgle/UNIQUE (user_id) &der er ingen falsk aggregering. Selvom du nogle gange kan fortryde sådan falsk sammenlægning.

En korrekt symmetrisk INNER JOIN-tilgang:LEFT JOIN q1 &q2--1:many--derefter GROUP BY &GROUP_CONCAT (hvilket er, hvad din første forespørgsel gjorde); derefter separat på samme måde LEFT JOIN q1 &q3--1:many--derefter GROUP BY &GROUP_CONCAT; derefter INNER JOIN de to resultater PÅ user_id--1:1.

En korrekt symmetrisk tilgang til skalar underforespørgsler:VÆLG GROUP_CONCAT'erne fra q1 som skalære underforespørgsler hver med en GROUP BY.

En korrekt kumulativ LEFT JOIN tilgang:LEFT JOIN q1 &q2--1:mange--derefter GROUP BY &GROUP_CONCAT; så VENSTRE JOIN det &q3--1:many--derefter GRUPPER EFTER &GROUP_CONCAT.

En korrekt tilgang som din 2. forespørgsel:Du LEFT JOIN q1 &q2--1:mange. Derefter LEVER DU JOIN det &q3--many:1:many. Det giver en række for hver mulig kombination af et tag og en kategori, der vises med et user_id. Derefter efter at du GRUPPER AF GRUPPE_CONCAT--over dublerede (bruger-id, tag) par og duplikere (bruger_id, kategori) par. Det er derfor, du har duplikerede listeelementer. Men tilføjelse af DISTINCT til GROUP_CONCAT giver et korrekt resultat. (Pr. wchiquito 's kommentar.)

Hvilket du foretrækker, er som sædvanligt en teknisk afvejning, der skal informeres om forespørgselsplaner og timings, i henhold til faktiske data/brug/statistik. input og statistik for forventet mængde af duplikering), timing af faktiske forespørgsler osv. Et spørgsmål er, om de ekstra rækker i mange:1:mange JOIN-tilgangen opvejer dets lagring af en 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 ;


  1. Opret en databasemailkonto i SQL Server (T-SQL)

  2. mySQL ::indsætte i tabel, data fra en anden tabel?

  3. Android Room Library kan ikke kopiere databasen fra aktiv

  4. Sådan tilføjes automatisk stigningskolonne i eksisterende tabel i MySQL