Sum results of a few queries and then find top 5 in SQL

ⅰ亾dé卋堺 提交于 2019-11-27 05:39:54

The question leaves room for interpretation. To UNION the resulting rows of all three queries and then pick the 5 rows with the highest "amounts":

(SELECT event_id, count(*) AS amount
FROM   pageview 
GROUP  BY event_id
ORDER  BY pageviews DESC, rand()
LIMIT  1000)

UNION ALL
(SELECT event_id, count(*)
FROM   upvote
GROUP  BY event_id
ORDER  BY upvotes DESC, rand()
LIMIT  1000)

UNION ALL
(SELECT event_id, count(*)
FROM   attending
GROUP  BY event_id
ORDER  BY attendants DESC, rand()
LIMIT  1000)

ORDER  BY 2 DESC
LIMIT  5;

The manual:

To apply ORDER BY or LIMIT to an individual SELECT, place the clause inside the parentheses that enclose the SELECT.

UNION ALL, so duplicates are not removed.


If you want to add the counts for every event_id, this query should do it:

SELECT event_id, sum(amount) AS total
FROM (
   (SELECT event_id, count(*) AS amount
    FROM   pageview 
    GROUP  BY event_id
    ORDER  BY pageviews DESC, rand()
    LIMIT  1000)

    UNION ALL
    (SELECT event_id, count(*)
    FROM   upvote
    GROUP  BY event_id
    ORDER  BY upvotes DESC, rand()
    LIMIT  1000)

    UNION ALL
    (SELECT event_id, count(*)
    FROM   attending
    GROUP  BY event_id
    ORDER  BY attendants DESC, rand()
    LIMIT  1000)
    ) x
GROUP  BY 1
ORDER  BY sum(amount) DESC
LIMIT  5;

The tricky part here is that not every event_id will be present in all three base queries. So you have to take care that a JOIN does not lose rows completely and additions don't turn out NULL.

Use UNION ALL, not UNION. You don't want to remove identical rows, you want to add them up.

The x is shorthand for AS x - a table alias. It is required for for a subquery to have a name. Can be any other name here.

The SOL-feature FULL OUTER JOIN is not implemented in MySQL (last time I looked), so you have to have to make do with UNION. FULL OUTER JOIN would join all three base queries without losing rows.

Answer to follow up question

SELECT event_id, sum(amount) AS total
FROM (
   (SELECT event_id, count(*) / 100 AS amount
    FROM   pageview ... )

    UNION ALL
    (SELECT event_id, count(*) * 5 
    FROM   upvote ... )

    UNION ALL
    (SELECT event_id, count(*) * 10
    FROM   attending ... )
    ) x
GROUP  BY 1
ORDER  BY  sum(amount) DESC
LIMIT  5;

Or, if you want to use the base counts in multiple ways:

SELECT event_id
      ,sum(CASE source
              WHEN 'p' THEN amount / 100
              WHEN 'u' THEN amount * 5
              WHEN 'a' THEN amount * 10
              ELSE 0
           END)  AS total
FROM (
   (SELECT event_id, 'p'::text AS source, count(*) AS amount
    FROM   pageview ... )

    UNION ALL
    (SELECT event_id, 'u'::text, count(*)
    FROM   upvote ... )

    UNION ALL
    (SELECT event_id, 'a'::text, count(*)
    FROM   attending ... )
    ) x
GROUP  BY 1
ORDER  BY 2 DESC
LIMIT  5;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!