I'm trying to solve this particular problem from PGExercises.com:
https://www.pgexercises.com/questions/aggregates/rankmembers.html
The gist of the question is that I'm given a table of club members and half hour time slots that they have booked (getting the list is a simple INNER JOIN of two tables).
I'm supposed to produce a descending ranking of members by total hours booked, rounded off to the nearest 10. I also need to produce a column with the rank, using the RANK()
window function, and sort the result by the rank. (The result produces 30 records.)
The author's very elegant solution is this:
select firstname, surname, hours, rank() over (order by hours) from
(select firstname, surname,
((sum(bks.slots)+5)/20)*10 as hours
from cd.bookings bks
inner join cd.members mems
on bks.memid = mems.memid
group by mems.memid
) as subq
order by rank, surname, firstname;
Unfortunately, as a SQL newbie, my very unelegant solution is much more convoluted, using CASE WHEN
and converting numbers to text in order to look at the last digit for deciding on whether to round up or down:
SELECT
firstname,
surname,
CASE
WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10
ELSE FLOOR(SUM(slots*0.5) /10) * 10
END AS hours,
RANK() OVER(ORDER BY CASE
WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10
ELSE FLOOR(SUM(slots*0.5) /10) * 10
END DESC) as rank
FROM cd.bookings JOIN cd.members
ON cd.bookings.memid = cd.members.memid
GROUP BY firstname, surname
ORDER BY rank, surname, firstname;
Still, I manage to almost get it just right - out of the 30 records, I get one edge case, whose firstname is 'Ponder' and lastname is 'Stephens'. His rounded number of hours is 124.5
, but the solution insists that rounding it to the nearest 10 should produce a result of 120
, whilst my solution produces 130
.
(By the way, there are several other examples, such as 204.5
rounding up to 210
both in mine and the exercise author's solution.)
What's wrong with my rounding logic?
If you want to round to the nearest 10, then use the built-in round()
function:
select round(<whatever>, -1)
The second argument can be negative, with -1 for tens, -2 for hundreds, and so on.