对于呼叫评级系统,我试图将电话呼叫持续时间分成不同资费周期的子持续时间。调用存储在SQL Server数据库中,具有启动时间和总持续时间。晚上(0000 - 0800),高峰(0800 - 1900)和非高峰(1900-235959)期间的价格不同。
例如: 通话时间为18:50:00,持续时间为1000秒。这将使通话在19:06:40结束,使得峰值关税为10分钟/ 600秒,非高峰关税为400秒。
显然,呼叫可以包裹无限数量的时段(我们不强制执行最大呼叫持续时间)。呼叫持续> 24小时可以包括所有3个时段,从高峰开始,经历非高峰,夜晚并返回高峰关税。
目前,我们正在使用VB中的递归计算不同的资费周期。我们计算在呼叫开始的相同资费周期内有多少呼叫进入,相应地改变呼叫的开始时间和持续时间并重复此过程直到呼叫的整个持续时间达到(peakDuration + offpeakDuration + nightDuration == callDuration)。
关于这个问题,我有两个问题:
是否可以在SQL Server语句中有效执行此操作? (我可以在存储过程中考虑子查询或大量编码,但这不会产生任何性能改进)
SQL Server是否能够以比当前VB脚本更具资源效率的方式进行此类计算?
答案 0 :(得分:2)
答案 1 :(得分:1)
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
}
答案 2 :(得分:0)
在T-SQL中有效吗?我怀疑没有,使用目前描述的架构。
但是,如果您的费率表存储每个日期的三个关税,则可能有可能。除了手头的问题之外,至少还有一个原因可以解决这个问题:在某个时刻,某个时期或其他时期的费率可能会发生变化,您可能需要获得历史性的费率。
所以说我们有这些表:
CREATE TABLE rates (
from_date_time DATETIME
, to_date_time DATETIME
, rate MONEY
)
CREATE TABLE calls (
id INT
, started DATETIME
, ended DATETIME
)
我认为有三种情况需要考虑(可能更多,我正在努力实现这一点):
假设速率是每秒,我认为您可能会产生类似以下(完全未经测试)的查询
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 BY或在代码中处理它。或者,通过精心构造的逻辑,您可以将UNIONed部分合并为具有大量AND和OR的单个WHERE子句。我认为UNION更清楚地表明了意图。
HTH&amp; HIW(希望它有用......)
答案 3 :(得分:0)
This is a thread关于我们在sqlteam.com上遇到的问题。看一看,因为它包含一些非常漂亮的解决方案。
答案 4 :(得分:0)
根据Mike Woodhouse的回答,这个可能为你工作:
SELECT id, SUM(DATEDIFF(ss, started, ended) * rate)
FROM rates
JOIN calls ON
CASE WHEN started < from_date_time
THEN DATEADD(ss, 1, from_date_time)
ELSE started > from_date_time
AND
CASE WHEN ended > to_date_time
THEN DATEADD(ss, -1, to_date_time)
ELSE ended END
< ended
GROUP BY id
答案 5 :(得分:0)
数据库中相关表的实际架构非常有用。我会尽我最大的猜测。我假设Rates表的start_time和end_time是午夜过后的分钟数。
使用日历表(在大多数数据库中有一个非常有用的表):
SELECT
C.id,
R.rate,
SUM(DATEDIFF(ss,
CASE
WHEN C.start_time < R.rate_start_time THEN R.rate_start_time
ELSE C.start_time
END,
CASE
WHEN C.end_time > R.rate_end_time THEN R.rate_end_time
ELSE C.end_time
END)) AS
FROM
Calls C
INNER JOIN
(
SELECT
DATEADD(mi, Rates.start_time, CAL.calendar_date) AS rate_start_time,
DATEADD(mi, Rates.end_time, CAL.calendar_date) AS rate_end_time,
Rates.rate
FROM
Calendar CAL
INNER JOIN Rates ON
1 = 1
WHERE
CAL.calendar_date >= DATEADD(dy, -1, C.start_time) AND
CAL.calendar_date <= C.start_time
) AS R ON
R.rate_start_time < C.end_time AND
R.rate_end_time > C.start_time
GROUP BY
C.id,
R.rate
我在打字时想出了这个,所以它没有经过测试,你很可能需要调整它,但希望你能看到一般的想法。
我也意识到你使用start_time和持续时间进行通话。您可以使用DATEADD(ss,C.start_time,C.duration)替换C.end_time,假设持续时间以秒为单位。
假设适当的索引等,这应该在任何体面的RDBMS中执行得非常快。
答案 6 :(得分:0)
如果您上次呼叫时间少于100
天:
WITH generate_range(item) AS
(
SELECT 0
UNION ALL
SELECT item + 1
FROM generate_range
WHERE item < 100
)
SELECT tday, id, span
FROM (
SELECT tday, id,
DATEDIFF(minute,
CASE WHEN tbegin < clbegin THEN clbegin ELSE tbegin END,
CASE WHEN tend < clend THEN tend ELSE clend END
) AS span
FROM (
SELECT DATEADD(day, item, DATEDIFF(day, 0, clbegin)) AS tday,
ti.id,
DATEADD(minute, rangestart, DATEADD(day, item, DATEDIFF(day, 0, clbegin))) AS tbegin,
DATEADD(minute, rangeend, DATEADD(day, item, DATEDIFF(day, 0, clbegin))) AS tend
FROM calls, generate_range, tariff ti
WHERE DATEADD(day, 1, DATEDIFF(day, 0, clend)) > DATEADD(day, item, DATEDIFF(day, 0, clbegin))
) t1
) t2
WHERE span > 0
我假设您将关税范围保持在午夜时分,并以分钟为单位计算长度。
答案 7 :(得分:0)
在数据库级别执行此类计算的一个主要问题是,在CPU和通过锁定的行和表的可用性方面,它会从数据库中获取资源。如果您在批处理操作中计算1,000,000个关税,那么这可能会在数据库上运行很长时间,在此期间您将无法使用该数据库进行任何其他操作。
如果您拥有资源,请使用一个事务检索所需的所有数据,并使用您选择的语言在数据库外部执行所有逻辑计算。然后插入所有结果。数据库用于存储和检索数据,并且它们执行的任何业务逻辑应始终保持绝对最低限度。虽然SQL在某些方面很出色,但它并不是日期或字符串处理工作的最佳语言。
我怀疑你已经在你的VBA工作中处于正确的位置,并且在不知道更多的情况下,它对我来说当然感觉像是一个递归的,或者至少是一个迭代的问题。正确完成后,递归可以成为解决问题的强大而优雅的解决方案。很少占用数据库的资源。