我试图找出SQL为每日配额系统执行运行总计。系统就像这样......
每天用户获得2个“消费品”的配额。如果他们全部使用它们,第二天他们会得到另一个2.如果他们以某种方式过度使用它们(使用超过2个),第二天他们仍然得到2(他们不能有负余额)。如果他们不全部使用它们,剩余部分将持续到第二天(可以进入下一天等等)。
以下是用作验证的数据图表。它被列为当天的配额,当天使用的金额,当天结束时留下的金额:
2 - 2 - 0
2 - 0 - 2
4 - 3 - 1
3 - 0 - 3
5 - 7 - 0
2 - 1 - 1
3 - 0 - 3
5 - 2 - 3
5 - 1 - 4
6 - 9 - 0
开始使用的SQL将是:
WITH t(x, y) AS (
VALUES (2, '2013-09-16'),
(0, '2013-09-17'),
(3, '2013-09-18'),
(0, '2013-09-19'),
(7, '2013-09-20'),
(1, '2013-09-21'),
(0, '2013-09-22'),
(2, '2013-09-23'),
(1, '2013-09-24'),
(9, '2013-09-25')
)
对于我的生活,尝试递归语句和窗口聚合,我无法弄清楚如何使它工作(但我当然可以看到模式)。
它应该类似于2 - x + SUM(上一行),但我不知道如何将其放入SQL中。
答案 0 :(得分:2)
尝试创建自定义聚合函数,如:
CREATE FUNCTION quota_calc_func(numeric, numeric, numeric) -- carry over, daily usage and daily quota
RETURNS numeric AS
$$
SELECT GREATEST(0, $1 + $3 - $2);
$$
LANGUAGE SQL STRICT IMMUTABLE;
CREATE AGGREGATE quota_calc( numeric, numeric ) -- daily usage and daily quota
(
SFUNC = quota_calc_func,
STYPE = numeric,
INITCOND = '0'
);
WITH t(x, y) AS (
VALUES (2, '2013-09-16'),
(0, '2013-09-17'),
(3, '2013-09-18'),
(0, '2013-09-19'),
(7, '2013-09-20'),
(1, '2013-09-21'),
(0, '2013-09-22'),
(2, '2013-09-23'),
(1, '2013-09-24'),
(9, '2013-09-25')
)
SELECT x, y, quota_calc(x, 2) over (order by y)
FROM t;
可能包含错误,尚未对其进行测试。
答案 1 :(得分:1)
他们不能有负余额
这引发了我的记忆: - )
10年前我在Teradata系统上遇到了类似的问题。
可以使用递归轻松实现逻辑,对于每一行都执行:
如果小于零,则添加2“new”和减去x“used”配额 改为使用零。
我不记得我是如何找到这个解决方案的,但我终于使用简单的累积总和实现了它:
SELECT
dt.*,
CASE -- used in following calculation, this is just for illustration
WHEN MIN(quota_raw) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING) >= 0 THEN 0
ELSE MIN(quota_raw) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING)
END AS correction,
quota_raw
- CASE
WHEN MIN(quota_raw) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING) >= 0 THEN 0
ELSE MIN(quota_raw) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING)
END AS quote_left
FROM
(
SELECT quota, datecol,
SUM(quota) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING) AS quota_used,
2*COUNT(*) OVER (ORDER BY datecol ROWS UNBOUNDED PRECEDING) AS quota_available,
quota_available - quota_used AS quota_raw
FROM t
) AS dt
ORDER BY datecol
秘诀就是移动的最小“校正”,它将负面结果调整为零。
答案 2 :(得分:0)
简单的递归cte解决方案,假设你在日期中没有间隙:
with recursive cte as (
select
t.dt,
2 as quote_day,
t.quote_used,
greatest(2 - t.quote_used, 0) as quote_left
from t
where t.dt = '2013-09-16'
union all
select
t.dt,
2 + c.quote_left as quote_day,
t.quote_used,
greatest(2 + c.quote_left - t.quote_used, 0) as quote_left
from cte as c
inner join t on t.dt = c.dt + 1
)
select *
from cte
<强> sql fiddle demo 强>
另一种解决方案 - 累积聚合:
with cte1 as (
select
dt, quote_used,
sum(2 - quote_used) over(order by dt asc) as quote_raw
from t
), cte2 as (
select
dt, quote_used, quote_raw,
least(min(quote_raw) over(order by dt asc), 0) as quote_corr
from cte1
)
select
dt,
quote_raw - quote_corr + quote_used as quote_day,
quote_used,
quote_raw - quote_corr as quote_left
from cte2
<强> sql fiddle demo 强>