在 SQL Server 中计算呼叫的不同资费周期

2024-04-09

对于呼叫评级系统,我试图将电话呼叫持续时间分成不同资费周期的子持续时间。呼叫存储在 SQL Server 数据库中,并具有开始时间和总持续时间。夜间 (0000 - 0800)、高峰 (0800 - 1900) 和非高峰 (1900-235959) 时段的费率有所不同。

例如: 呼叫从 18:50:00 开始,持续时间为 1000 秒。这将使通话在 19:06:40 结束,高峰资费为 10 分钟/600 秒,非高峰资费为 400 秒。

显然,一次呼叫可以无限数量地循环(我们不强制规定最大呼叫持续时间)。持续 > 24 小时的通话可以包含所有 3 个时段,从高峰时段开始,经过非高峰时段、夜间时段,然后回到高峰时段。

目前,我们正在 VB 中使用递归计算不同的关税期。我们计算通话开始时在同一资费周期内进行的通话量,相应地更改通话的开始时间和持续时间,然后重复此过程,直到达到通话的全部持续时间(peakDuration + offpeakDuration + nightDuration ==通话持续时间)。

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

  • 是否有可能做到这一点有效地在 SQL Server 语句中? (我可以想到存储过程中的子查询或大量编码,但这不会产生任何性能改进)

  • SQL Server 是否能够以比当前 VB 脚本更节省资源的方式进行此类计算?


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

  1. 确定电话呼叫的哪些部分在何时使用哪些费率。
  2. 将每个费率的时间相加。

第 1 阶段比第 2 阶段更棘手。我在 IBM Informix Dynamic Server (IDS) 中运行了该示例,因为我没有 MS SQL Server。这些想法应该很容易翻译。 INTO TEMP 子句创建一个具有适当模式的临时表;该表是会话私有的,并在会话结束时消失(或者您明确删除它)。在 IDS 中,您还可以使用显式 CREATE TEMP TABLE 语句,然后使用 INSERT INTO temp-table SELECT ... 作为执行与 INTO TEMP 相同工作的更详细方法。

正如在 SO 的 SQL 问题中经常出现的那样,您没有向我们提供模式,因此每个人都必须发明一个可能与您所描述的内容匹配或不匹配的模式。

假设您的数据位于两个表中。第一个表是通话记录记录,通话的基本信息,例如拨打电话的电话、被叫号码、通话开始时间和通话时长:

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 表的副本:

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;

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

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);

我争论了费率表应该使用 TIME 还是 INTERVAL 值;在这种情况下,时间与相对于午夜的间隔非常相似,但间隔可以添加到时间戳,而时间则不能。我坚持使用《时间》,但这让事情变得一团糟。

此查询的棘手部分是在没有循环的情况下生成每个费率的相关日期和时间范围。事实上,我最终使用嵌入在存储过程中的循环来生成整数列表。 (我还使用了 IBM Informix Dynamic Server IDS 特有的技术,使用系统目录中的表 ID 号作为 1..N 范围内的连续整数源,适用于版本中从 1 到 60 的数字11.50。)

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;

在简单的情况下(也是最常见的情况),呼叫属于单一资费周期;多时段的通话增添了兴奋感。

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

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
);

幸运的是,您没有提到周末费率,因此您向客户收取相同的费用

周末的价格与一周内的价格相同。然而,答案应该适应这样的

如果可能的话。如果你要变得像提供周末费率一样复杂

公共假期,但在圣诞节或新年期间,您将收取高峰费率,而不是

由于需求量很大,周末费率很高,那么您最好将费率存储在永久的 rates_date_time 表中。

填充关税日期时间的第一步是生成与呼叫相关的日期列表:

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 中)。 步骤integers生成从 0 到调用涵盖的天数的值,并将结果存储在临时表中。对于多条记录的更一般情况,最好计算最小和最大日期并生成中间的日期,而不是多次生成日期,然后使用 DISTINCT 子句消除它们。

现在使用费率表与 call_dates 表的笛卡尔积来生成每天的费率信息。这是关税时间作为间隔更整齐的地方。

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;

现在我们需要将通话记录记录与适用的资费进行匹配。该条件是处理重叠的标准方法 - 如果第一个时间段的结束晚于第二个时间段的开始并且第一个时间段的开始早于第二个时间段的结束,则两个时间段重叠:

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;

然后我们需要确定费率的开始和结束时间。费率的开始时间是资费开始时间和通话开始时间中较晚的一个。资费结束时间为资费结束时间和通话结束时间中较早的一个:

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) 的结果是 INTERVAL,而不是数字,因此我必须调用转换函数将 INTERVAL 转换为十进制秒数,而该(非标准)函数是 iv_seconds:

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;

对于示例数据,这产生了数据(为了简洁起见,我没有打印电话号码和被叫号码):

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 中是必要的 - 直接迭代就足够了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 SQL Server 中计算呼叫的不同资费周期 的相关文章

  • 如何设计一个存储非常大数据的表?

    我需要在Oracle中设计一个表 每天将存储2 5TB的数据 它可以增长到 200TB 超过 200TB 时记录将被清除 将其保留在 OLTP 中是一个可行的选择 还是需要将其转移到数据仓库 DB 请建议我在设计该表或数据库的架构时应牢记的
  • 使用 impala 按范围连接表的有效方法

    我第一个有下表 Range 包括值范围和附加列 row From To Country 1 1200 1500 2 2200 2700 3 1700 1900 4 2100 2150 The From and Toare bigint并且是
  • T-SQL参数嗅探重新编译计划

    我有 SQL 命令 exec sp executesql N SELECT TOP 10 FROM mytableView WHERE Name LIKE Value0 ORDER BY Id DESC N Value0 varchar 5
  • 我可以将 UseCSharpNullComparisonBehavior 用于单个查询吗?

    我有一个查询 该查询曾经是存储过程 现已转换为 EF 查询 现在已经超时了 使用 SQL Profiler 我可以看到生成的 SQL 的唯一区别是 EF 转变的新行为entity Property value into entity Pro
  • SQL Union All 查询中的排序规则冲突

    有一个 Union All 查询 如下所示 当在 SQL Server 中触发时 出现错误 无法解决 SELECT 语句中第 1 列的排序规则冲突 请问 在哪里添加带有此 Union All 查询的 Collat e database de
  • SQL Server xp_delete_file 参数

    谁能解释一下清单xp Delete filesql server 中的参数 我没有找到此存储过程的 msdn 文档 我从维护计划中获取了 xp Delete file 脚本 但不理解第一个参数 xp delete file取五个参数 文件类
  • 删除 SQL 中重复的字段条目

    无论如何 我可以删除某个表中的所有重复条目 users 这是我拥有的条目类型的示例 我必须说一下桌子users由3个字段组成 ID user and pass mysql query DELETE FROM users WHERE or d
  • 如何在Django模型中通过“TIME_ZONE”分别将当前正确的日期和时间设置为“DateField()”和“TimeField()”作为默认值?

    医生在下面说DateField auto now add https docs djangoproject com en 4 2 ref models fields django db models DateField auto now a
  • 将 UTC 日期转换为 PHP 本地时间

    我使用以下方法将 UTC 日期存储到数据库中 utc gmdate M d Y h i s A 然后我想将保存的 UTC 日期转换为客户端的本地时间 我怎样才能做到这一点 Thanks 如果客户端指的是浏览器 那么您首先需要从浏览器将时区名
  • 为什么没有主键的表是一个坏主意?

    我对数据建模非常陌生 根据微软的实体框架 不允许使用没有主键的表 这显然是一个坏主意 我试图找出为什么这是一个坏主意 以及如何修复我的模型 这样我就不会出现这个漏洞 我当前的模型中有 4 个表 User City HelloCity 和 R
  • 如何以 HH:MM 表示小数时间

    我有一个十进制格式的时间 22 13以小时为单位 我想在 R 中将其表达为HH MM 我是说 22 08 贬值秒 有人可以帮助我吗 语法 02 0f是将值四舍五入为整数并使其始终为 2 个单位 所以 7 8分钟将转换为字符 08 x lt
  • SQL Server批量上传策略

    我使用以下函数将数据从 CSV 上传到 SQL 表 有更好的方法吗 我现在担心连接长时间保持 因此需要减少 public bool SaveProxyBulkUploadData List
  • 如何将 RFC 2822 日期/时间解析为 Python 日期时间?

    我有 RFC 2822 指定的表格日期 比如说Fri 15 May 2009 17 58 28 0000 作为字符串 有没有一种快速和 或标准的方法来将其作为datetimePython 2 5 中的对象 我尝试生成 strptime 格式
  • SQL Server 中临时表的使用

    这是一个悬而未决的问题 但我真的很想听听人们的意见 我很少使用显式声明的临时表 表变量或常规 tmp 表 因为我相信不这样做会导致更简洁 可读和可调试的 T SQL 我还认为 在需要时 例如当您在查询中使用派生表时 SQL 可以比我更好地利
  • CHAR(64) 或 BINARY(32) 在 SQL SERVER 中存储 SHA256 哈希

    我正在讨论在 SQL Server 中存储 SHA256 哈希时使用哪种数据类型 应该是 CHAR 64 还是 BINARY 32 该列将成为唯一聚集索引的一部分 我知道此时我可能会吹毛求疵 但是我想第一次就把这件事做好 而且我知道有时原始
  • Android接收短信Intent:获取消息Id或线程Id

    我已经注册了一个广播监听器来接收android provider Telephony SMS DELIVER意向行动 我似乎能够获取这条新消息的正文和发件人电话号码 但是我无法获取此新消息的消息 ID 或线程 ID 我发现的唯一方法是搜索消
  • 在 Ruby 中将 Time 类对象转换为 RFC3339

    谷歌日历 API v2 https developers google com google apps calendar v2 developers guide protocol的时间相关查询需要采用 RFC3339 格式 当我在 需要 时
  • 使用 java 中的准备好的语句插入自定义 SQL 类型

    我有一些自定义类型 它们基本上都是枚举 以下是它们的外观示例 CREATE TYPE card suit AS ENUM spades clubs hearts diamonds 我在 Java 中有一些准备好的语句 看起来像这样 Setu
  • 如何使 cx-oracle 将查询结果绑定到字典而不是元组?

    这是我的代码 我想找到一种方法将查询结果作为字典列表而不是元组列表返回 看起来 cx oracle 通过部分文档讨论 绑定 来支持这一点 虽然我不知道它是如何工作的 def connect dsn cx Oracle makedsn hos
  • SQL - 每个级别都有记录的递归树层次结构

    尝试使用 SAS 据我所知 不支持WITH RECURSIVE 在 SQL 中创建经典的层次结构树 这是现有表中的简化数据结构 USER ID SUPERVISOR ID 因此 要构建层次结构 您只需递归连接 x 次即可获取您要查找的数据

随机推荐

  • 硬币兑换 DP 解决方案以跟踪硬币

    尝试为一般的硬币找零问题编写一个 DP 解决方案 该解决方案还可以跟踪使用了哪些硬币 到目前为止 我已经可以为我提供所需的最低数量的硬币 但无法弄清楚如何获取使用了哪些硬币以及使用了多少次 如果使用硬币 我尝试设置另一个带有值的表 布尔值
  • Javascript 域模型对象约定

    如果我必须在 C 中创建域模型对象 我可能会这样做 public class Person Public string Name get set Public string Gender get set Public int Age get
  • 使用文本框自动滚动到底部

    我有一个由 ms access 制作的 mdb 文件 它有一个表单 表单内部有一个大文本框 制作此文本框的目的是通过在文本框中添加消息来显示某些工作的进度 txtStatus value txtStatus value Doing some
  • 如何通过 COM 公开 .netstandard2.0 库以便在 VB6 中使用?

    我有一个 dotnet 核心库 一个框架 4 7 2 库和一个 vb6 应用程序 我想编写一个公共库供他们所有人访问 因此选择 netstandard2 0 我尝试了 netstandard2 0 库和 vb6 之间的 4 7 2 框架包装
  • Grails:两个域对象之间的多重关系

    我正在尝试在 Grails 中的两个域类之间实现两种不同类型的关系 考虑以下 我有两个域类 一个作者类和一个书籍类 其中一个作者拥有很多书籍 class Author String name class Book String title
  • 在 SQL Management studio 中更改 CommandTimeout

    如何更改 SQL Management Studio 中的 CommandTimeout 如果您在使用表设计器时遇到超时 请更改工具 gt 选项 gt 设计器 gt 表和数据库设计器下的 事务超时时间 值 这将消除此消息 超时已到 操作完成
  • pandas 六个月的日期范围

    所以 这是我的数据框 PatientNumber QT Answer Answerdate DiagnosisDate 1 1 transferring No 2017 03 03 2018 05 03 2 1 preparing food
  • epoll_wait()接收socket关闭两次(read()/recv()返回0)

    我们有一个使用 epoll 来侦听和处理 http 连接的应用程序 有时 epoll wait 会连续两次收到 fd 上的 close 事件 含义 epoll wait 返回连接 fd 其中 read recv 返回 0 这是一个问题 因为
  • pg (node-postgres) 是否自动清理数据

    我在用node postgres对于生产应用程序 我想知道是否有什么我应该关心的 数据是否自动清理node postgres 我在 github 页面上找不到任何相关信息 https github com brianc node postg
  • BrokeredMessage Azure ServiceBus 的虚假传递和接收

    我创建了一个 BrokeredMessage 实例 并希望围绕它的传递计数与队列的最大传递计数来测试我的代码 我不想建立一个真正的队列来发送和接收消息 但在消息传递之前 deliverycount 属性不会初始化 我怎样才能伪造这个 我设法
  • Ninject:将某物与其自身绑定是什么意思?

    Ninject 具有以下功能self binding like Bind
  • Rollup 函数 - 替换 NULL

    我的 SQL 的最后部分似乎遇到了麻烦 当在我的 SQL 中使用汇总函数实现分组依据时 会出现 NULL 如何修改 NULL 以便在此聚合 SQL 中将其替换为 TOTAL Current table returned Name Activ
  • 在Python中将数据作为后台进程写入磁盘

    我有一个 Python 程序 基本上执行以下操作 for j in xrange 200 1 Compute a bunch of data 2 Write data to disk 1 大约需要2 5分钟2 大约需要 1分钟 请注意 内存
  • 如何在多线程模式下使用 Gunicorn 运行 Flask

    我有用 Flask 编写的网络应用程序 正如大家所建议的 我不能在生产中使用 Flask 所以我想到了枪与烧瓶 在 Flask 应用程序中 我正在加载一些机器学习模型 它们的总大小为 8GB 我的 Web 应用程序的并发性可以达到1000
  • Laravel 中的菜单逻辑应该放在哪里?

    在 Laravel 中放置菜单数据逻辑的最佳概念位置是什么 如果我使用菜单包放在哪里 在Base Controller创建额外的功能或不同的东西 Note 这个答案是为 Laravel 3 编写的 可能适用于最新的 Laravel 4 也可
  • numpy.random.choice 的性能

    我更新了代码和时间 我正在尝试提高代码中函数的性能 我必须生成一个包含随机元素的列表 但是 列表的不同部分必须填充来自不同集合的元素 代码示例如下 我必须生成数百万个这样的列表 一次一个 函数 foo1 是最快的 但它不能满足我的需要 它可
  • 应用程序因“正在发送信号”而终止。但没有例外或其他信息

    我正在开发一个通过蓝牙记录数据的应用程序 但在收集数据数小时后它会间歇性崩溃 使得很难追踪错误 logcat 输出不是很有帮助 https i stack imgur com rDZbB png https i stack imgur co
  • CSRF 和 X-CSRF-Token 的区别

    使用上有什么区别X CSRF Token在 HTTP 标头中或token在隐藏字段中 何时使用隐藏字段 何时使用标头以及为什么 我觉得X CSRF Token是当我使用 JavaScript AJAX 但我不确定时 CSRF 保护有多种方法
  • Android init.rc 服务套接字数据流,“连接被拒绝”

    我正在开发一个使用 tcpdump 获取网络数据的项目 为了简化问题 让我们用我编写的脚本替换 tcpdump system bin sh while do echo print out from socket sleep 1 done 我
  • 在 SQL Server 中计算呼叫的不同资费周期

    对于呼叫评级系统 我试图将电话呼叫持续时间分成不同资费周期的子持续时间 呼叫存储在 SQL Server 数据库中 并具有开始时间和总持续时间 夜间 0000 0800 高峰 0800 1900 和非高峰 1900 235959 时段的费率