sql - 在 SQL 中从下到上将给定数量分配给负值列表

标签 sql oracle

我有一个给定日期的负值表,如下所示:

------------------
| date | deficit |    budget: 100
|----------------|
|01.01 |   -5    |
|01.02 |   -7    |
|        .       |
|        .       |
|20.12 |   -8    |
|30.12 |   -6    |
------------------

我也有一个固定的数字,我们就称之为预算,并说它是 100。

现在我想用我的预算来抵消负值,例如我将预算从 100 减少到 95,并在表格中日期为 01.01 的行中写入 0,而不是 5。

像这样:

------------------
| date | deficit |    budget: 95
|----------------|
|01.01 |    0    |
|01.02 |   -7    |
|        .       |
|        .       |
|20.12 |   -8    |
|30.12 |   -6    |
------------------

我想从最低日期到最高日期按顺序执行此操作,如果我用完了预算,只需在末尾留下我无法再填写的负值即可。

最后,它看起来像这样,假设我们以 6 的预算到达 20.12 条目,因此无法完全抵消负值并耗尽预算

------------------
| date | deficit |    budget: 0
|----------------|
|01.01 |    0    |
|01.02 |    0    |
|        .       |
|        .       |
|20.12 |   -2    |
|30.12 |   -6    |
------------------

我该怎么做?我知道如何用命令式语言来做到这一点,但是如何确保它以何种顺序进行减去并以主要是声明性语言改变总和的状态? 我正在使用甲骨文。

编辑:

这是我想要在命令式伪代码中执行的操作:

*Asuming deficits are already ordered by date
foreach deficit of deficits
  if (budget - deficit) >= 0
    set deficit = 0 
    set budget = budget - deficit
  else 
    set deficit = budget - deficit   
    set budget = 0
  end if 

所以我基本上需要这个,但是在 SQL 代码中。

最佳答案

有几种方法可以解决这个问题,但您可以使用窗口分析函数来获取赤字的运行总额以及已花费的预算数量。要获得您想要的最终数字,您可以使用以下内容:

greatest(deficit,
  sum(deficit) over (order by date_col)
    + least(100, - sum(deficit) over (order by date_col)))

或者也许更清楚一点:

case
  when sum(deficit)
    over (order by date_col rows between unbounded preceding and 1 preceding) <= -100
  then
    deficit
  else
    sum(deficit) over (order by date_col)
      + least(100, - sum(deficit) over (order by date_col))
end

sum(deficit) o​​ver (order by date_col) 为您提供按日期顺序排列的赤字运行总计。对于第一个日期,计算为-5;第二个为 (-5 + -7) = -12;等等

least(100, - sum(deficit) o​​ver (order by date_col))) 为您提供截至当前日期(包括当前日期)已使用的预算,但上限为 100。对于第一个日期是 least(100, - (-5)) ,即 5;第二个是 least(100, - (-5 + -7)),即 12;到 20 号,它是 least(100, - (-102)),因此您得到 100 和 102 中较小的一个 - 因此上限为 100。(您可以这样做 greatest(- 100,sum(deficit) o​​ver (order by date_col))) 如果您愿意,然后在最后一步中减去它。)

当将其添加到赤字的运行总计中时,前几行会被抵消,结果为零。对于 20 日,赤字总数为 102,上限值为 100,因此最终结果为 -2。但对于 30 号,这会给你 100 - 108,所以 -8 而不是你真正想要的 -6。这就是 greatest(deficit, ...) 发挥作用的地方。直到 20 号,答案都不会改变; greatest(-8, 100 - 102) 仍然是 -2,因为 -2 > -8。对于第 30 个,您的 greatest(-6, 100 - 108) 为 -6,因为 -6 > -8。

带有一些示例数据的演示并显示了一些工作:

-- sample data
with your_table (date_col, deficit) as (
            select date '2020-01-01', -5 from dual
  union all select date '2020-01-02', -7 from dual
  union all select date '2020-01-03', -50 from dual
  union all select date '2020-01-04', -32 from dual
  union all select date '2020-01-20', -8 from dual
  union all select date '2020-01-30', -6 from dual
)
-- actual query
select date_col, deficit,
  sum(deficit) over (order by date_col) as sum_deficit,
  least(100, - sum(deficit) over (order by date_col)) as budget_used,
  greatest(0, 100 + sum(deficit) over (order by date_col)) as budget_left_1,
  100 - least(100, - sum(deficit) over (order by date_col)) as budget_left_2,
  greatest(deficit,
    sum(deficit) over (order by date_col)
      + least(100, - sum(deficit) over (order by date_col))) as new_deficit_1,
  case
    when sum(deficit)
      over (order by date_col rows between unbounded preceding and 1 preceding) <= -100
    then
      deficit
    else
      sum(deficit) over (order by date_col)
        + least(100, - sum(deficit) over (order by date_col))
    end as new_deficit_2
from your_table
order by date_col;

产生:

DATE_COL      DEFICIT SUM_DEFICIT BUDGET_USED BUDGET_LEFT_1 BUDGET_LEFT_2 NEW_DEFICIT_1 NEW_DEFICIT_2
---------- ---------- ----------- ----------- ------------- ------------- ------------- -------------
2020-01-01         -5          -5           5            95            95             0             0
2020-01-02         -7         -12          12            88            88             0             0
2020-01-03        -50         -62          62            38            38             0             0
2020-01-04        -32         -94          94             6             6             0             0
2020-01-20         -8        -102         100             0             0            -2            -2
2020-01-30         -6        -108         100             0             0            -6            -6

关于sql - 在 SQL 中从下到上将给定数量分配给负值列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60296517/

相关文章:

mysql - 使用存储过程游标存储数据的问题

sql - 甲骨文移动平均线

oracle - 从 Oracle 调用 ffmpeg api

mysql - 像 Oracle 一样在 mysql 中截断日期字段

php - 我的 SELECT 查询失败,我该如何解决?

MySQL SELECT 中的公共(public)外键

sql - 如何在查询过程中清除 SQL 查询的结果消息数据?

sql - 注册 SQL CLR 存储过程中使用的程序集

sql - 如何获取oracle sql中指定位置具有特定字符的记录

where 子句中带有相关子查询的 SQL 查询