我试图从PGExercises.com解决这个特殊问题:
https://www.pgexercises.com/questions/aggregates/rankmembers.html
问题的要点是我给了一张俱乐部成员表和他们预订的半小时时段(获取列表是两个表的简单内部联接)
我应该按总小时预订产生降序排名,并四舍五入到最近的10 。我还需要使用RANK()
窗口函数生成具有等级的列,并按等级对结果进行排序。 (结果产生30条记录。)
作者非常优雅的解决方案就是:
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;
不幸的是,作为一个SQL新手,使用CASE WHEN
并将数字转换为文本以查看最后一个数字以决定是否围绕向上1}},我的非常不合理的解决方案更复杂。 em>或 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;
尽管如此,我设法几乎把它弄得恰到好处 - 在30条记录中,我得到了一个边缘案例,其名字是'思考'和姓氏是' Stephens'。他的小时数为124.5
,但解决方案坚持将其四舍五入到最接近的10会产生120
的结果,而我的解决方案产生130
。
(顺便说一句,还有其他一些例子,例如204.5
在我和演习作者的解决方案中四舍五入到210
。)
我的舍入逻辑有什么问题?
答案 0 :(得分:10)
如果要舍入到最接近的10,请使用内置的round()
函数:
select round(<whatever>, -1)
第二个参数可以是负数,-1表示数十,-2表示数百,依此类推。
答案 1 :(得分:2)
要舍入到10
的最近倍数:
round(<value> / 10 - .5) * 10
将以向上舍入以5-9结尾的值,否则将向下。
这个通用公式为:
round(<value> / <range> - .5) * <range>
如果你愿意,你可以四舍五入到最近的13
。
答案 2 :(得分:0)
我一直在努力解决同等问题。我需要将数字舍入到最接近的50的倍数。戈登的建议在这里不起作用。
我的第一次尝试是SELECT round(120 / 50) * 50
,它提供了100
。但是,SELECT round(130 / 50) * 50
给了100
。这是错的;最近的倍数是150
。
诀窍是使用浮点数划分,例如SELECT round(130 / 50.0) * 50
将提供150
。
事实证明,x/y
和x
为整数,y
等同于trunc(x/y)
。浮点除法正确舍入到最接近的倍数。
答案 3 :(得分:0)
我认为波希米亚的公式不正确。
通用公式为:
round((value +(range / 2))/ range)*范围
因此要转换为最接近的50,取整((103 + 25)/ 50)* 50->将得到100