MySQL: необходимо вернуть топ-3 пользователей с наибольшим количеством голосов.Результаты нужны в одном столбце из суммы двух подзапросов.Java / Spring MVC - PullRequest
0 голосов
/ 29 мая 2018

У меня есть блог Spring MVC с функциями голосования по почте и комментариям.Я хочу вернуть трех лучших пользователей в зависимости от количества голосов, полученных ими за все свои сообщения и комментарии.

таблиц:

пользователей u [id, имя пользователя]

сообщений p [id, u.id]

комментарии c [id, p.id, u.id]

post_votes pv [p.id, u.id, тип (1 или -1)]

comment_votes cv [c.id, u.id, type (1 или -1)]

Следующее утверждение дает мне общее количество голосов на пользователя, запрашивая две отдельные таблицы голосования и затем суммируя вместе:

SELECT
  (SELECT SUM(type) 
  FROM posts_votes pv 
  JOIN posts p ON p.id = pv.post_id 
  JOIN users u ON u.id = p.user_id  
  WHERE u.id LIKE ?1)
+
  (SELECT SUM(type) 
  FROM comments_votes cv  
  JOIN comments c ON c.id = cv.comment_id 
  JOIN users u ON u.id = c.user_id 
  WHERE u.id LIKE ?1)

Это прекрасно работает с предложением WHERE для каждого идентификатора пользователя ... Но сейчас я пытаюсь найти только трех лучших пользователей, которые имеют наибольшее количество голосов, и у меня слишком много трудностей.Это то, что я имею до сих пор:

 SELECT u.id, u.username, IFNULL(SUM(pv.type), 0) AS totalPostVotes
 FROM posts_votes pv
 JOIN posts p ON p.id = pv.post_id
 JOIN users u ON u.id = p.user_id
 GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 3

Вышеприведенное утверждение работает само по себе, давая мне: u.id, u.username и totalPostVote в порядке убывания.То же самое, что ниже для комментариев:

 SELECT u.id, u.username, IFNULL(SUM(cv.type), 0) AS totalCommentVotes
 FROM comment_votes cv
 JOIN comments c ON c.id = cv.comment_id
 JOIN users u ON u.id = c.user_id
 GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 3

Отлично!Но я хочу, чтобы результат третьего столбца SUM был по существу «totalVotes» и содержал сумму обоих этих подзапросов.Тогда я сгруппируюсь по u.id ЗАКАЗАТЬ ПО totalVotes DESC LIMIT 3.

Примерно так:

  SELECT u.id, u.username, SUM(
                         (SELECT IFNULL(SUM(pv.type), 0) AS totalPostVotes
                          FROM posts_votes pv
                            JOIN posts p ON p.id = pv.post_id
                            JOIN users u ON u.id = p.user_id
                          GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 1)
                         +
                         (SELECT IFNULL(SUM(cv.type), 0) AS totalCommentVotes
                          FROM comments_votes cv                       
                            JOIN comments c ON c.id = cv.comment_id
                            JOIN users u ON u.id = c.user_id
                          GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 1))
   AS totalVotes from users u
   GROUP BY u.id, u.username ORDER BY totalVotes DESC LIMIT 3

id | username | totalVotes
2   user2       11
1   user1       11
29  user29      11

То, что происходит, является результатом того, что totalVotes действительно является правильным подсчетом голосов, 11для «верхнего» пользователя, но ни один из этих пользователей не является настоящим топ-пользователем, и правильный голос повторяется 3 раза под видом других пользователей.Я даже не уверен, как пользователи сортируются в этот момент, потому что они не в порядке, который я узнаю.

Подзапросы работают отдельно (они дают мне правильного пользователя), когда я добавляю SELECT "u.id, u.username" IFNULL (SUM ()), но затем, если я запускаю весь блок, я получаю ошибку«Операнд должен содержать 1 столбец (и)». Поэтому я удаляю их и возвращаюсь только к SELECT IFNULL (SUM ())

Я также замечаю, что подзапросам разрешен только LIMIT 1. Как бы я получил верх3, тогда?Должен ли я где-нибудь сделать СОЮЗ или достаточно «+»?Это довольно запутанно.Может кто-нибудь, пожалуйста, помогите мне с этим?Любая помощь приветствуется.Заранее спасибо!

Обновлен код, спасибо Питеру:

     SELECT
     u.username,
     pv_sum.total AS postTotal,
     cv_sum.total AS commentTotal,
     IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
     FROM users u
     LEFT JOIN (
          SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
          FROM posts p
            JOIN posts_votes pv ON pv.post_id = p.id
          GROUP BY p.user_id
        ) pv_sum ON pv_sum.user_id = u.id
     LEFT JOIN (
          SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
          FROM comments c
            JOIN comments_votes cv ON cv.comment_id = c.id
          GROUP BY c.user_id
        ) cv_sum ON cv_sum.user_id = u.id
     GROUP BY u.username, postTotal, commentTotal
     ORDER BY totalVotes DESC LIMIT 3;

1 Ответ

0 голосов
/ 29 мая 2018

Не помещайте свои подзапросы в вашу SELECT-часть, а присоединяйте их к таблице пользователей:

SELECT 
    u.username, 
    pv_sum.total AS postTotal, 
    cv_sum.total as commentTotal, 
    IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
FROM users u
LEFT JOIN (
   SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
    FROM posts p 
    JOIN post_votes pv ON pv.post_id = p.id
    GROUP BY p.user_id
) pv_sum ON pv_sum.user_id = u.id
LEFT JOIN (
    SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
    FROM comments c 
    JOIN comment_votes cv ON cv.comment_id = c.id
    GROUP BY c.user_id
) cv_sum ON cv_sum.user_id = u.id
GROUP BY u.id
ORDER BY totalVotes DESC
LIMIT 3;

Fiddle: http://sqlfiddle.com/#!9/980cb2/11

...