在 Postgres 中将数字四舍五入到最接近的 10
Rounding numbers to the nearest 10 in Postgres
我正在尝试解决来自 PGExercises.com 的这个特定问题:
https://www.pgexercises.com/questions/aggregates/rankmembers.html
问题的要点是,我得到了 table 个俱乐部成员和他们预订的 半小时 个时间段(获取列表是一个两个 tables).
的简单 INNER JOIN
我应该按总预订时数 对会员进行降序排列,四舍五入到最接近的 10。我还需要使用 RANK()
window 函数生成一个带有排名的列,然后按排名对结果进行排序。 (结果产生 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
并将数字转换为文本以查看最后一位数字来决定是否四舍五入向上 或 向下:
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 条记录中,我得到了一个边缘案例,其 firstname 是 'Ponder' 和 姓是'Stephens'。他的四舍五入小时数是 124.5
,但解决方案坚持将其四舍五入到最接近的 10 应该产生 120
的结果,而我的解决方案产生 130
.
(顺便说一句,还有其他几个例子,例如 204.5
四舍五入到 210
在我的和练习作者的解决方案中。)
我的舍入逻辑有什么问题?
如果要四舍五入到最接近的 10,请使用内置的 round()
函数:
select round(<whatever>, -1)
第二个参数可以是负数,-1代表十,-2代表百,依此类推。
舍入到任何数字(范围)的最接近倍数:
round(<value> / <range>) * <range>
“最接近的”表示恰好范围边界之间一半的值被四舍五入。
这适用于任意范围,如果您愿意,您也可以四舍五入到最接近的 13
或 0.05
:
round(64 / 10) * 10 —- 60
round(65 / 10) * 10 —- 70
round(19.49 / 13) * 13 -- 13
round(19.5 / 13) * 13 -- 26
round(.49 / .05) * .05 -- 0.5
round(.47 / .05) * .05 -- 0.45
我一直在努力解决类似的问题。我需要将数字四舍五入到最接近的 50 的倍数。Gordon 在这里的建议不起作用。
我的第一次尝试是 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)
。其中浮动除法正确舍入到最接近的倍数。
我认为 Bohemian 的公式不正确。
通用公式为:
round((值 + (range/2))/范围) * 范围
因此要转换为最接近的 50,round((103 + 25)/50) * 50 --> 将得到 100
我正在尝试解决来自 PGExercises.com 的这个特定问题:
https://www.pgexercises.com/questions/aggregates/rankmembers.html
问题的要点是,我得到了 table 个俱乐部成员和他们预订的 半小时 个时间段(获取列表是一个两个 tables).
的简单 INNER JOIN我应该按总预订时数 对会员进行降序排列,四舍五入到最接近的 10。我还需要使用 RANK()
window 函数生成一个带有排名的列,然后按排名对结果进行排序。 (结果产生 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
并将数字转换为文本以查看最后一位数字来决定是否四舍五入向上 或 向下:
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 条记录中,我得到了一个边缘案例,其 firstname 是 'Ponder' 和 姓是'Stephens'。他的四舍五入小时数是 124.5
,但解决方案坚持将其四舍五入到最接近的 10 应该产生 120
的结果,而我的解决方案产生 130
.
(顺便说一句,还有其他几个例子,例如 204.5
四舍五入到 210
在我的和练习作者的解决方案中。)
我的舍入逻辑有什么问题?
如果要四舍五入到最接近的 10,请使用内置的 round()
函数:
select round(<whatever>, -1)
第二个参数可以是负数,-1代表十,-2代表百,依此类推。
舍入到任何数字(范围)的最接近倍数:
round(<value> / <range>) * <range>
“最接近的”表示恰好范围边界之间一半的值被四舍五入。
这适用于任意范围,如果您愿意,您也可以四舍五入到最接近的 13
或 0.05
:
round(64 / 10) * 10 —- 60
round(65 / 10) * 10 —- 70
round(19.49 / 13) * 13 -- 13
round(19.5 / 13) * 13 -- 26
round(.49 / .05) * .05 -- 0.5
round(.47 / .05) * .05 -- 0.45
我一直在努力解决类似的问题。我需要将数字四舍五入到最接近的 50 的倍数。Gordon 在这里的建议不起作用。
我的第一次尝试是 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)
。其中浮动除法正确舍入到最接近的倍数。
我认为 Bohemian 的公式不正确。
通用公式为:
round((值 + (range/2))/范围) * 范围
因此要转换为最接近的 50,round((103 + 25)/50) * 50 --> 将得到 100