sql >> Databasteknik >  >> RDS >> Mysql

Konstigt dubblettbeteende från GROUP_CONCAT av två LEFT JOINs av GROUP_BYs

Din andra fråga är av 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 inre GROUP BYs resulterar i (user_id, t.tag) &(user_id, c.category) vara nycklar/UNIKA. Utöver det kommer jag inte att ta upp dessa GROUP BYs.

TL;DR När du går med (q1 JOIN q2) till q3 finns det inte på en nyckel/UNIQUE av en av dem så för varje user_id får du en rad för varje möjlig kombination av tagg &kategori. Så den slutliga GROUP BY matar in dubbletter per (user_id, tagg) &per (user_id, kategori) och olämpligt GROUP_CONCATs dubbletter av taggar och kategorier per user_id. Rätt skulle vara (q1 JOIN q2 GROUP BY) JOIN (q1 JOIN q3 GROUP BY) där alla kopplingar är på gemensam nyckel/UNIQUE (user_id) &det finns ingen falsk aggregering. Även om du ibland kan ångra sådan falsk sammanläggning.

En korrekt symmetrisk INNER JOIN-metod:LEFT JOIN q1 &q2--1:många--sedan GROUP BY &GROUP_CONCAT (vilket är vad din första fråga gjorde); sedan separat på samma sätt LEFT JOIN q1 &q3--1:många--sedan GROUP BY &GROUP_CONCAT; sedan INNER JOIN de två resultaten PÅ user_id--1:1.

En korrekt symmetrisk skalär subquery-metod:VÄLJ GROUP_CONCAT från q1 som skalära subqueries var och en med en GROUP BY.

En korrekt kumulativ LEFT JOIN-metod:LEFT JOIN q1 &q2--1:många--sedan GROUP BY &GROUP_CONCAT; sedan VÄNSTER JOIN det &q3--1:många--sedan GRUPPERA AV &GROUP_CONCAT.

Ett korrekt tillvägagångssätt som din andra fråga:Du VÄNSTER JOIN q1 &q2--1:många först. Sedan LÄNSTER du GÅ MED DET &q3--många:1:många. Det ger en rad för varje möjlig kombination av en tagg och en kategori som visas med ett user_id. Sedan efter att du GRUPPERAT AV du GROUP_CONCAT--över dubbletter (user_id, tag) par och dubbletter (user_id, kategori) par. Det är därför du har dubbletter av listelement. Men att lägga till DISTINCT till GROUP_CONCAT ger ett korrekt resultat. (Per wchiquito s kommentar.)

Vilket du föredrar är som vanligt en teknisk kompromiss som ska informeras av frågeplaner och tidpunkter, per faktisk data/användning/statistik. indata och statistik för förväntad mängd duplicering), tidpunkt för faktiska frågor, etc. En fråga är om de extra raderna i tillvägagångssättet många:1:många JOIN kompenserar för att spara 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. 1064-fel i CREATE TABLE ... TYPE=MYISAM

  2. Att få resultatet av dynamisk SQL till en variabel för sql-server

  3. Kan jag återställa en enskild tabell från en fullständig mysql mysqldump-fil?

  4. SQLite Var