Reputation: 71
I'm currently learning MySQL and am working on a query that displays the top 5 and bottom 5 categories and groups by joining 2 tables. What I have meets the requirements but I want to display it more cleanly. I've got this to display by using a union but was wondering if I could show the results as four columns instead for a cleaner look. 2 columns related to the top 5 and 2 related to the bottom five categories determined by the number of groups in each category.
Current query:
SELECT*
FROM(SELECT
category_name,
count(category_name) AS NumOfGroups
From
category c
JOIN
grp g ON c.category_id=g.category_id
GROUP BY category_name
order by NumOfGroups desc
LIMIT 5) most
UNION
SELECT *
FROM (SELECT
category_name,
count(category_name) AS NumOfGroups
From
category c
JOIN
grp g ON c.category_id=g.category_id
GROUP BY category_name
ORDER BY NumOfGroups ASC
LIMIT 5) Least;
This displays:
category NumOfGroups
Tech 911
Food & Drink 790
Photography 320
Outdoors & Adventure 218
Games 166
Singles 4
Fitness 15
Paranormal 16
Fashion & Beauty 26
Movements & Politics 32
Can I take this one step further to display a result like below? Would I have to transpose?
Desired result:
category NumOfGroups category NumOfGroups
Tech 911 Singles 4
Food & Drink 790 Fitness 15
Photography 320 Paranormal 16
Outdoors & Adventure 218 Fashion & Beauty 26
Games 166 Movements & Politics 32
Upvotes: 0
Views: 547
Reputation: 817
IMO, this is best done at the application level rather than in your database queries. Using each tool as it's designed results in cleaner solutions. However, if you really need to do this in mysql, you can generate row numbers in each of your subqueries and join them to make a unified result.
set @row:=0;
set @row2:=0;
SELECT most.category_name,most.members,least.category_name,least.members
FROM (
SELECT *,@row := @row + 1 as rownum
FROM (
SELECT
category_name,
count(*) numberOfGroups,
FROM category c
JOIN grp g ON c.category_id=g.category_id
GROUP by category_name
ORDER BY numberOfGroups DESC
LIMIT 5
) temp
) most
LEFT JOIN (
SELECT *,@row2 := @row2 + 1 as rownum
FROM (
SELECT
category_name,
count(*) numberOfGroups
FROM category c
JOIN grp g ON c.category_id=g.category_id
GROUP by category_name
ORDER BY numberOfGroups ASC
LIMIT 5
) temp
) least
ON most.rownum=least.rownum;
There's still a caveat where the "most" subquery needs to always be >= the number of row results relative to "least" or you'll get clipping. As long as it's always 5 though (as it appears to be very likely in your case), you'll be safe.
Upvotes: 0
Reputation: 164164
Create a CTE
where you use ROW_NUMBER()
window function twice to rank the rows based on the value of NumOfGroups
and then do a self join:
WITH cte AS (
SELECT c.category_name, COUNT(*) NumOfGroups,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rn_most,
ROW_NUMBER() OVER (ORDER BY COUNT(*)) rn_least
FROM category c JOIN grp g
ON c.category_id = g.category_id
GROUP BY c.category_name
)
SELECT c1.category_name category_most, c1.NumOfGroups NumOfGroups_most,
c2.category_name category_least, c2.NumOfGroups NumOfGroups_least
FROM cte c1 INNER JOIN cte c2
ON c2.rn_least = c1.rn_most
WHERE c1.rn_most <= 5
ORDER BY c2.rn_least
Upvotes: 1