计算通话时长的数学公式

7

几年前,我曾在一家电信公司工作,需要编写一个公式来计算通话的持续时间,按照以下算法:

  • t1是第一个时段
  • t2是重复时段
  • RCT是实际通话时间(以秒为单位)
  • CD是有效通话时长(用于计费目的)

如果RCT小于t1,则CD等于t1
如果RCT大于t1,则CD = t1 + x*t2,其中x将“舍入”到下一个最高的t2倍数。

该算法的意思是:“在前t1秒内收费,然后每隔t2秒再收费一次”。

例如:

t1  t2  RCT CD  
60  10  48  60
60  10  65  70
60  10  121 130
30  20  25  30
30  20  35  50
30  20  65  70

你能创建一个函数/SQL语句来返回“通话持续时间”CD吗?
不使用if then else语句...?
3个回答

4
假设是整数列:
SELECT t1
    ,t2
    ,RCT
    CASE
    WHEN RCT < t1
        THEN t1 
    ELSE
        t1 + t2 * ((RCT - t1) / t2 + SIGN((RCT - t1) % t2))
    END AS CD

但我想还有一个情况,让我看看能否解决它。

仅使用整数算术(仍不符合ANSI标准):

SELECT  t1
       ,t2
       ,RCT
       ,CD
       ,t1 + SIGN(RCT / t1) * t2 * ((RCT - t1) / t2 + SIGN((RCT - t1) % t2)) AS CalcCD
FROM    Calls

谢谢,但我认为它仍然比我想象中要混乱。将其转换为浮点数会更清晰,但对于大查询(如电话呼叫)来说,我敢打赌速度会明显变慢。 - Cade Roux
啊好吧,不需要过早优化 :) 如果 OP 最终需要在不到一秒钟的时间内计算十亿条目,他将不得不发布另一个问题来改善效率! - e.James

2

编辑:进一步简化,并修复了< 与 <= 的错误。

无浮点数,适用于我能访问的所有数据库:

create table calls (t1 int, t2 int, rct int, cd int)

insert into calls (t1, t2, rct, cd) 
values (60, 10, 48, 60)

insert into calls (t1, t2, rct, cd) 
values (60, 10, 65, 70)

insert into calls  (t1, t2, rct, cd)
values (60, 10, 121, 130)

insert into calls  (t1, t2, rct, cd)
values (30, 20, 25, 30)

insert into calls  (t1, t2, rct, cd)
values (30, 20, 35, 50)

insert into calls  (t1, t2, rct, cd)
values (30, 20, 65, 70)

--Additional test to show that it works
insert into calls  (t1, t2, rct, cd)
values (60, 10, 70, 70)

select t1, t2, rct, cd, 
t1 + case when rct <= t1 
  then 0 
  else ( (rct-1-t1) / t2 + 1) * t2 end as CalceCD
from calls

结果:

t1          t2          rct         cd          CalceCD
----------- ----------- ----------- ----------- -----------
60          10          48          60          60
60          10          65          70          70
60          10          121         130         130
30          20          25          30          30
30          20          35          50          50
30          20          65          70          70
60          10          70          70          70
(6 行受影响)

您可以自由地创建函数作为UDF或任何SQL环境允许的内容,以清理选择。

编辑:是的,使用floor和偏移量1可以避免浮点数运算。


这不应该是 ceiling((rct-t1)/t2) 而不是 (floor((rct-t1)/t2)+1) 吗?否则,60-10-70 将会产生一张 80 的 CD。 - Ben Blank
插入到calls表中的值为(60, 10, 70, 70) 插入到calls表中的值为(60, 10, 80, 80) 这些不起作用。 - Cade Roux
这些插入假定一种语法,可以暗示表格的格式。如果需要,请添加(t1、t2、rct、cd)。 - Godeke
使用floor函数进行一些“减1”操作可以避免浮点数计算,而且应该适用于任何情况。错误在于rct-1-t1中缺少了-1。 - Godeke
顺便说一下,你可以像我一样通过乘以SIGN(RCT / t1)来消除CASE。 - Cade Roux
显示剩余4条评论

2
我会使用:

t1 + t2*ceiling( (rct - t1 + abs(rct - t1))*1.00/(2*t2) )

或者:

t1 + t2*ceiling( Cast((rct - t1 + abs(rct - t1)) as float)/(2*t2) )

你缺少一个左括号。 - Cade Roux
我尝试在几个地方放置它,但结果不符合。 - Cade Roux
我不确定*1.00是否有助于整数列问题。有人可以为我验证一下吗? - e.James
是的 - 1.00强制转换,很好。 - Cade Roux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接