Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Hành vi trùng lặp kỳ lạ từ GROUP_CONCAT của hai LEFT JOIN trong GROUP_BY

Truy vấn thứ hai của bạn có dạng:

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

Các GROUP BY bên trong dẫn đến (user_id, t.tag) &(user_id, c.category) là khóa / DUY NHẤT. Ngoài ra, tôi sẽ không giải quyết các GROUP BY đó.

TL; DR Khi bạn nối (q1 JOIN q2) với q3, nó không nằm trên một khóa / DUY NHẤT của một trong số chúng, vì vậy đối với mỗi user_id, bạn nhận được một hàng cho mọi kết hợp thẻ &danh mục có thể có. Vì vậy, GROUP BY cuối cùng nhập các thẻ &danh mục trùng lặp cho mỗi (user_id, tag) &per (user_id, category) và các thẻ &danh mục trùng lặp không thích hợp GROUP_CONCAT cho mỗi user_id. Đúng sẽ là (q1 JOIN q2 GROUP BY) JOIN (q1 JOIN q3 GROUP BY) trong đó tất cả các liên kết đều nằm trên khóa chung / UNIQUE (user_id) &không có tập hợp giả mạo. Mặc dù đôi khi bạn có thể hoàn tác tập hợp giả mạo như vậy.

Cách tiếp cận INNER JOIN đối xứng đúng:LEFT JOIN q1 &q2--1:many - sau đó GROUP BY &GROUP_CONCAT (đó là những gì truy vấn đầu tiên của bạn đã làm); sau đó riêng biệt tương tự LEFT JOIN q1 &q3--1:many - sau đó GROUP BY &GROUP_CONCAT; sau đó INNER THAM GIA hai kết quả BẬT user_id - 1:1.

Cách tiếp cận truy vấn con vô hướng đối xứng đúng:CHỌN GROUP_CONCATs từ q1 là truy vấn con vô hướng mỗi người có một GROUP BY.

Cách tiếp cận LEFT JOIN tích lũy đúng:LEFT JOIN q1 &q2--1:many - then GROUP BY &GROUP_CONCAT; sau đó THAM GIA TRÁI &q3--1:nhiều - sau đó NHÓM THEO &GROUP_CONCAT.

Một cách tiếp cận đúng như truy vấn thứ 2 của bạn:Đầu tiên bạn TRÁI THAM GIA q1 &q2--1:many. Sau đó, bạn THAM GIA TRÁI mà &q3 - nhiều:1:nhiều. Nó cung cấp một hàng cho mọi kết hợp có thể có của thẻ &danh mục xuất hiện với user_id. Sau đó, sau khi bạn GROUP BY bạn GROUP_CONCAT - qua các cặp trùng lặp (user_id, tag) và các cặp trùng lặp (user_id, category). Đó là lý do tại sao bạn có các phần tử danh sách trùng lặp. Nhưng thêm DISTINCT vào GROUP_CONCAT sẽ cho kết quả chính xác. (Theo wchiquito nhận xét của.)

Như thường lệ, bạn thích đánh đổi kỹ thuật để được thông báo bằng kế hoạch và thời gian truy vấn, theo dữ liệu thực tế / sử dụng / thống kê. đầu vào &số liệu thống kê cho lượng trùng lặp dự kiến), thời gian của các truy vấn thực tế, v.v. Một vấn đề là liệu các hàng bổ sung của phương pháp tiếp cận many:1:many có bù đắp cho việc tiết kiệm được một GROUP BY hay không.

-- 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL / MariaDB - sắp xếp theo truy vấn con bên trong

  2. Làm cách nào để kết nối ứng dụng Android với cơ sở dữ liệu MySQL?

  3. Cho phép người dùng MySQL tạo cơ sở dữ liệu nhưng chỉ cho phép truy cập vào cơ sở dữ liệu của riêng họ

  4. Khôi phục người dùng và mật khẩu 'root' đã xóa cho MySQL

  5. Làm cách nào để thực hiện chèn hàng loạt trong mySQL bằng node.js