首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >计算Server中调用的不同关税期

计算Server中调用的不同关税期
EN

Stack Overflow用户
提问于 2009-02-25 11:31:56
回答 8查看 3.7K关注 0票数 2

对于呼叫评级系统,我试图将电话通话的持续时间划分为不同收费期的次持续时间。调用存储在Server数据库中,并具有启动时间和总持续时间。夜间(0000 - 0800)、高峰(0800-1900)和非高峰(1900-235959)期间的费率不同。

例如:呼叫从18:50:00开始,持续时间为1000秒。这样的话,通话就会在19:06:40结束,峰值收费是10分钟/ 600秒,非高峰收费是400秒。

显然,调用可以覆盖无限的时间段(我们不强制执行最大调用持续时间)。一个持续时间超过24小时的呼叫可以覆盖所有三个时段,从高峰开始,经过非高峰、夜间和返回高峰关税。

目前,我们正在使用VB中的递归计算不同的关税期.我们计算调用在相同的关税期内的调用量,相应地更改呼叫的启动时间和持续时间,并重复此过程,直到达到呼叫的全部持续时间(peakDuration + offpeakDuration + nightDuration == callDuration)。

关于这个问题,我有两个问题:

  • 是否有可能在Server语句中有效地执行?(我可以想到存储过程中的子查询或大量编码,但这不会产生任何性能改进)
  • Server是否能够以比当前VB脚本更有效的资源方式进行这样的计算?
EN

回答 8

Stack Overflow用户

发布于 2009-03-01 10:14:04

在我看来,这是一个分两个阶段的行动。

  1. 确定电话的哪个部分使用哪个速率在什么时间。
  2. 每个费率中的时间之和。

第一阶段比第二阶段更棘手,我在(IDS)中使用过这个示例,因为我没有。这些想法应该很容易翻译。INTO TEMP子句创建一个具有适当架构的临时表;该表对于会话是私有的,并且在会话结束时消失(或者显式地删除它)。在IDS中,您还可以使用一个显式的创建临时表语句,然后插入到临时表选择.作为一种更冗长的方式来完成和临时工作相同的工作。

在SQL问题中,关于SO的问题经常发生,您没有提供模式,因此每个人都必须发明一个模式,该模式可能与您所描述的匹配,也可能与您所描述的不匹配。

假设您的数据位于两个表中。第一个表包含呼叫日志记录、有关所打电话的基本信息,如打电话的电话、被呼叫的号码、呼叫开始的时间和通话的持续时间:

代码语言:javascript
运行
复制
CREATE TABLE clr  -- call log record
(
    phone_id      VARCHAR(24) NOT NULL,   -- billing plan
    called_number VARCHAR(24) NOT NULL,   -- needed to validate call
    start_time    TIMESTAMP   NOT NULL,   -- date and time when call started
    duration      INTEGER     NOT NULL    -- duration of call in seconds
                  CHECK(duration > 0),
    PRIMARY KEY(phone_id, start_time)
    -- other complicated range-based constraints omitted!
    -- foreign keys omitted
    -- there would probably be an auto-generated number here too.
);
INSERT INTO clr(phone_id, called_number, start_time, duration)
    VALUES('650-656-3180', '650-794-3714', '2009-02-26 15:17:19', 186234);

为了方便(主要是为了节省多次写入加法),我想要一个clr表的副本,其中包含实际的结束时间:

代码语言:javascript
运行
复制
SELECT  phone_id, called_number, start_time AS call_start, duration,
        start_time + duration UNITS SECOND AS call_end
    FROM clr
    INTO TEMP clr_end;

关税数据存储在一个简单的表格中:

代码语言:javascript
运行
复制
CREATE TABLE tariff
(
    tariff_code   CHAR(1)      NOT NULL   -- code for the tariff
                  CHECK(tariff_code IN ('P','N','O'))
                  PRIMARY KEY,
    rate_start    TIME         NOT NULL,  -- time when rate starts
    rate_end      TIME         NOT NULL,  -- time when rate ends
    rate_charged  DECIMAL(7,4) NOT NULL   -- rate charged (cents per second)
);
INSERT INTO tariff(tariff_code, rate_start, rate_end, rate_charged)
    VALUES('N', '00:00:00', '08:00:00', 0.9876);
INSERT INTO tariff(tariff_code, rate_start, rate_end, rate_charged)
    VALUES('P', '08:00:00', '19:00:00', 2.3456);
INSERT INTO tariff(tariff_code, rate_start, rate_end, rate_charged)
    VALUES('O', '19:00:00', '23:59:59', 1.2345);

我讨论了关税表是否应该使用时间或间隔值;在这种情况下,时间非常类似于相对于午夜的时间间隔,但时间间隔可以添加到时间戳中,而时间不能。我坚持时间,但这让事情变得很混乱。

这个查询的棘手部分是为每个没有循环的关税生成相关的日期和时间范围。事实上,我最终使用了一个嵌入在存储过程中的循环来生成一个整数列表。(我还使用了一种特定于,IDS的技术,它使用来自系统目录的表ID号作为范围1.N中连续整数的来源,它适用于11.50版中的1到60之间的数字。)

代码语言:javascript
运行
复制
CREATE PROCEDURE integers(lo INTEGER DEFAULT 0, hi INTEGER DEFAULT 0)
    RETURNING INT AS number;
    DEFINE i INTEGER;
    FOR i = lo TO hi STEP 1
        RETURN i WITH RESUME;
    END FOR;
END PROCEDURE;

在简单情况下(也是最常见的情况),呼叫属于单一关税期;多期呼叫增加了兴奋。

让我们假设我们可以创建一个与此模式匹配的表表达式,并涵盖我们可能需要的所有时间戳值:

代码语言:javascript
运行
复制
CREATE TEMP TABLE tariff_date_time
(
     tariff_code   CHAR(1)      NOT NULL,
     rate_start    TIMESTAMP    NOT NULL,
     rate_end      TIMESTAMP    NOT NULL,
     rate_charged  DECIMAL(7,4) NOT NULL
);

幸运的是,你没有提到周末的价格,所以你对顾客的收费是一样的

周末和一周的费率。然而,答案应该适应这样的情况。

如果有可能的话。如果你要像给周末的价格一样复杂

公众假期,除了在圣诞节或新年,你收取最高的费用,而不是

周末费率由于需求高,那么最好将利率存储在一个永久的tariff_date_time表中。

填充tariff_date_time的第一步是生成与调用相关的日期列表:

代码语言:javascript
运行
复制
SELECT DISTINCT EXTEND(DATE(call_start) + number, YEAR TO SECOND) AS call_date
    FROM clr_end,
         TABLE(integers(0, (SELECT DATE(call_end) - DATE(call_start) FROM clr_end)))
         AS date_list(number)
    INTO TEMP call_dates;

这两个日期值之间的差异是一个整数的天数(在IDS中)。过程整数生成从0到调用所涵盖的天数的值,并将结果存储在临时表中。对于多条记录的更一般情况,最好是计算最小和最大日期,并在两者之间生成日期,而不是多次生成日期,然后使用DISTINCT子句删除它们。

现在,使用关税表的笛卡儿乘积和call_dates表生成每天的汇率信息。这是每隔一段时间关税时间将更加整洁的地方。

代码语言:javascript
运行
复制
SELECT  r.tariff_code,
        d.call_date + (r.rate_start - TIME '00:00:00') AS rate_start,
        d.call_date + (r.rate_end   - TIME '00:00:00') AS rate_end,
        r.rate_charged
    FROM call_dates AS d, tariff AS r
    INTO TEMP tariff_date_time;

现在,我们需要将呼叫日志记录与适用的费率相匹配。该条件是处理重叠问题的一种标准方法--如果第一个时间段的结束时间晚于第二个时间段的开始,而第一个时间段的开始时间在第二个时间段结束之前,则两个时间段重叠:

代码语言:javascript
运行
复制
SELECT tdt.*, clr_end.*
FROM tariff_date_time tdt, clr_end
WHERE tdt.rate_end > clr_end.call_start
  AND tdt.rate_start < clr_end.call_end
INTO TEMP call_time_tariff;

然后我们需要确定费率的开始和结束时间。费率的起始时间是费率开始时间的后一段时间和呼叫的开始时间。费率的结束时间是关税结束时间和通话结束时间的较早时间:

代码语言:javascript
运行
复制
SELECT  phone_id, called_number, tariff_code, rate_charged,
        call_start, duration,
        CASE WHEN rate_start < call_start THEN call_start
        ELSE rate_start END AS rate_start,
        CASE WHEN rate_end >= call_end THEN call_end
        ELSE rate_end END AS rate_end
    FROM call_time_tariff
    INTO TEMP call_time_tariff_times;

最后,我们需要将每一税率所花费的时间相加,并将时间(以秒为单位)乘以收费率。由于SUM的结果(rate_end- rate_start)是一个间隔,而不是一个数字,所以我不得不调用一个转换函数来将这个区间转换成一个十进制秒数,而这个(非标准的)函数是iv_seconds:

代码语言:javascript
运行
复制
SELECT phone_id, called_number, tariff_code, rate_charged,
       call_start, duration,
       SUM(rate_end - rate_start) AS tariff_time,
       rate_charged * iv_seconds(SUM(rate_end - rate_start)) AS tariff_cost
   FROM call_time_tariff_times
   GROUP BY phone_id, called_number, tariff_code, rate_charged,
            call_start, duration;

对于示例数据,这产生了数据(在这里,我没有打印电话号码和电话号码以保证紧凑性):

代码语言:javascript
运行
复制
N   0.9876   2009-02-26 15:17:19   186234   0 16:00:00   56885.760000000
O   1.2345   2009-02-26 15:17:19   186234   0 10:01:11   44529.649500000
P   2.3456   2009-02-26 15:17:19   186234   1 01:42:41  217111.081600000

这是一个非常昂贵的电话,但电信公司会对此感到满意的。您可以查看任何中间结果,以了解如何得出答案。您可以使用更少的临时表,而代价是更清晰。

对于单个调用,这与在客户端运行VB中的代码没有太大不同。对于很多电话来说,这可能会更有效率。我远不相信递归在VB中是必要的--直接迭代就足够了。

票数 2
EN

Stack Overflow用户

发布于 2011-04-12 19:10:14

代码语言:javascript
运行
复制
kar_vasile(id,vid,datein,timein,timeout,bikari,tozihat)
{
--- the bikari field is unemployment time  you can delete any where
select
            id,
            vid,
            datein,
            timein,
            timeout,
            bikari,
            hourwork =
            case when 
            timein <= timeout
            then
                SUM 
            (abs(DATEDIFF(mi, timein, timeout)) - bikari)/60 --
            calculate Hour 
        else
            SUM(abs(DATEDIFF(mi, timein, '23:59:00:00') + DATEDIFF(mi, '00:00:00', timeout) + 1) - bikari)/60 --
            calculate
            minute
                end
                ,
                minwork =
            case when 
            timein <= timeout
            then
                SUM 
            (abs(DATEDIFF(MI, timein, timeout)) - bikari)%60  --
            calclate Hour 
            starttime is later
            than endtime 
        else
            SUM(abs(DATEDIFF(mi, timein, '23:59:00:00') + DATEDIFF(mi, '00:00:00', timeout) + 1) - bikari)%60--
            calculate minute 
            starttime is later
            than
            endtime
                end, tozihat 

            from kar_vasile 
            group
            by id, vid, datein, timein, timeout, tozihat, bikari
}
票数 1
EN

Stack Overflow用户

发布于 2009-02-25 11:53:41

在T中有效吗?根据目前描述的模式,我怀疑没有。

但是,如果您的费率表存储每个日期的三个关税,则可能是可能的。除了手头的问题之外,至少有一个原因可以解释为什么你会这么做:在某个时候,一个时期的利率可能会发生变化,而你可能需要有历史上的利率。

假设我们有这些桌子:

代码语言:javascript
运行
复制
CREATE TABLE rates (
    from_date_time DATETIME
,   to_date_time DATETIME
,   rate MONEY
)

CREATE TABLE calls (
    id INT
,   started DATETIME
,   ended DATETIME
)

我认为有三种情况需要考虑(可能更多,我正在编造):

  1. 呼叫完全在一个速率期间内发生。
  2. 呼叫从一个速率周期(a)开始,在下一个(b)期间结束。
  3. 呼叫至少跨越一个完整的速率周期。

假设速率为每秒,我认为您可能会生成如下(完全未经测试的)查询

代码语言:javascript
运行
复制
SELECT id, DATEDIFF(ss, started, ended) * rate /* case 1 */
FROM rates JOIN calls ON started > from_date_time AND ended < to_date_time
UNION
SELECT id, DATEDIFF(ss, started, to_date_time) * rate /* case 2a and the start of case 3 */
FROM rates JOIN calls ON started > from_date_time AND ended > to_date_time
UNION
SELECT id, DATEDIFF(ss, from_date_time, ended) * rate /* case 2b and the last part of case 3 */
FROM rates JOIN calls ON started < from_date_time AND ended < to_date_time
UNION
SELECT id, DATEDIFF(ss, from_date_time, to_date_time) * rate /* case 3 for entire rate periods, should pick up all complete periods */
FROM rates JOIN calls ON started < from_date_time AND ended > to_date_time

您可以在SQL中应用SUM..GROUP或在代码中处理它。或者,使用精心构造的逻辑,您可能可以将UNIONed部件合并成一个包含大量and和OR的WHERE子句。我认为工会更清楚地表明了这一意图。

HTH & HIW (希望它能起作用)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/585645

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档