【问题标题】:Rounding numbers to the nearest 10 in Postgres在 Postgres 中将数字四舍五入到最接近的 10
【发布时间】:2017-05-03 18:47:14
【问题描述】:

我正在尝试从 PGExercises.com 解决这个特殊问题:

https://www.pgexercises.com/questions/aggregates/rankmembers.html

问题的要点是,我得到了一张俱乐部成员表和他们预订的 半小时 个时间段(获取列表是两个表的简单 INNER JOIN)。

我应该按预订的总小时数对成员进行降序排列,四舍五入到最近的 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 并将数字转换为文本,以便查看最后一位数字以决定是否进行四舍五入 向下

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' 而 lastname 是 '斯蒂芬斯。他的四舍五入小时数是124.5,但解决方案坚持将其四舍五入到最接近的10应该产生120的结果,而我的解决方案产生130

(顺便说一句,还有其他几个例子,比如我的和练习作者的解决方案中的204.5向上舍入到210。)

我的舍入逻辑有什么问题?

【问题讨论】:

    标签: sql postgresql integer window-functions integer-division


    【解决方案1】:

    四舍五入到任意数字(范围)的最近倍数:

    round(<value> / <range>) * <range>
    

    “最近”表示恰好位于范围边界之间一半的值被四舍五入。

    这适用于任意范围,如果您愿意,也可以四舍五入到最接近的 130.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
    

    【讨论】:

    • 如果你尝试SELECT round(0.49/0.05 - 0.5)*0.05; 给出0.45 而不是0.50
    • @sparkle 我已经意识到通用公式不应该在轮函数调用中进行减法运算。这有效:round(.49 / .05) * .05。感谢您提醒我。
    【解决方案2】:

    我不认为波西米亚的公式是正确的。

    广义公式为:

    round((值 + (range/2))/range) * 范围

    所以要转换为最接近的 50,round((103 + 25)/50) * 50 --> 将得到 100

    【讨论】:

      【解决方案3】:

      我一直在为同样的问题苦苦挣扎。我需要将数字四舍五入到最接近的 50 倍数。Gordon 的建议在这里不起作用。

      我的第一次尝试是SELECT round(120 / 50) * 50,它给出了100。但是,SELECT round(130 / 50) * 50 给了100。这是错误的;最接近的倍数是150

      诀窍是使用浮点数进行除法,例如SELECT round(130 / 50.0) * 50 将给150

      原来x/y,其中xy 是整数,等效于trunc(x/y)。浮点除法正确四舍五入到最接近的倍数。

      【讨论】:

        【解决方案4】:

        如果要四舍五入到最接近的 10,则使用内置的round() 函数:

        select round(<whatever>, -1)
        

        第二个参数可以是负数,-1 代表十位,-2 代表百位,以此类推。

        【讨论】:

          猜你喜欢
          • 2013-08-31
          • 2011-03-21
          • 2017-07-26
          • 2010-09-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多