【问题标题】:Add first row decimal value to next rows in SQL Server将第一行十进制值添加到 SQL Server 中的下一行
【发布时间】:2020-01-08 19:27:43
【问题描述】:

我有如下表格,其中包含该期间的客户用电量。可用数据,如

OwnerID StartDate   EndDate     Volume
---------------------------------------
 1      2019-01-01  2019-01-15  10.40
 1      2019-01-16  2019-01-31   5.80
 1      2019-02-01  2019-02-10   7.90
 1      2019-02-11  2019-02-28   8.50 

对于这个OwnerID = 1,现有一些体积为 0.90。这应该应用第一行(添加)

  1. 获取整数和小数部分值
  2. 返回的第一行十进制值应添加到下一行,反之亦然。

现有体积 - 0.90

所以预期的结果集应该是这样的:

OwnerId StartDate   EndDate     CalulatedVolume  AppliedExistingVolume(0.90)   RemainExistingVolume 
----------------------------------------------------------------------------------------------------
    1   2019-01-01  2019-01-15         11                0.60                   0.30
    1   2019-01-16  2019-01-31          6                0.20                   0.10
    1   2019-02-01  2019-02-10          8                0.10                   0.00
    1   2019-02-11  2019-02-28          8                0.00                   0.50 

您能建议如何在 SQL 查询中实现这一点吗?

【问题讨论】:

  • 为什么 10.4 到 11 而 8.5 到 8?这也与您的其他问题非常相似,但您尚未接受该答案。
  • 将 ExistingVolume 0.90 添加到第一个值 10.4
  • 这似乎是个好问题。如果你只是解释每一行的输出并增加兴奋度,那就太好了。谢谢。

标签: sql sql-server


【解决方案1】:

下面的示例使用累积和和一些 CTE 来计算这两列。

样本数据:

create table yourtable
(
  Id int identity(1,1) primary key,
  OwnerID int not null, 
  StartDate date not null, 
  EndDate date not null, 
  Volume decimal(16,2) not null
);

insert into yourtable
 (OwnerID, StartDate, EndDate, Volume) values       
  (1, '2019-01-01', '2019-01-15', 10.40)
, (1, '2019-01-16', '2019-01-31', 5.80)
, (1, '2019-02-01', '2019-02-10', 7.90)
, (1, '2019-02-11', '2019-02-28', 8.50)
;

查询:

;with CTE_EXISTING_VOLUMES AS
(
   select *
   from (values
     (1, 0.9)
   ) q(OwnerId, ExistingVolume)
)
, CTE_DATA AS
(
  select t.*, c.ExistingVolume
  , ceiling(t.Volume) - t.Volume as VolumeRemainder
  , sum(ceiling(t.Volume) - t.Volume) 
      over (partition by t.OwnerID
            order by t.StartDate) as CumSumVolumeRemainder
  from yourtable t
  left join CTE_EXISTING_VOLUMES c
    on t.OwnerID = c.OwnerID
)
, CTE_DATA2 AS
(
  select *
  , ceiling(Volume) as CalculatedVolume
  , IIF(ExistingVolume < CumSumVolumeRemainder, 0.0, (ceiling(Volume) - Volume)) as AppliedExistingVolume
  from CTE_DATA
)
select OwnerId, StartDate, EndDate
, CalculatedVolume
, AppliedExistingVolume
, case
  when AppliedExistingVolume > 0
  then ExistingVolume - CumSumVolumeRemainder
  else VolumeRemainder
  end as RemainExistingVolume
from CTE_DATA2;

GO
所有者 ID |开始日期 |结束日期 |计算量 |应用现有卷 | RemainExistingVolume ------: | :----------------- | :----------------- | :--------------- | :-------------------- | :-------------------- 1 | 01/01/2019 00:00:00 | 15/01/2019 00:00:00 | 11 | 0.60 | 0.30 1 | 16/01/2019 00:00:00 | 31/01/2019 00:00:00 | 6 | 0.20 | 0.10 1 | 01/02/2019 00:00:00 | 2019 年 10 月 2 日 00:00:00 | 8 | 0.10 | 0.00 1 | 2019 年 11 月 2 日 00:00:00 | 28/02/2019 00:00:00 | 9 | 0.00 | 0.50

db小提琴here

【讨论】:

    【解决方案2】:

    当需求绝对明确时,您可以考虑更好的解决方案,即Set Based Approach

    当需求不是很清楚时,你会想到像RBAR这样的解决方案。

    假设我要在 Excel 中计算输出,那么我将应用什么公式?

    必须有一些公式,但要求不明确。

    create table #temp(OwnerID int, StartDate date,EndDate date,Volume decimal(5,2))
    insert into #temp values
      (1,'2019-01-01','2019-01-15', 10.40)
     ,(1,'2019-01-16','2019-01-31', 5.80)
     ,(1,'2019-02-01','2019-02-10', 7.90)
     ,(1,'2019-02-11','2019-02-28', 8.50) 
    
     declare @ExistingVolume decimal(5,2)=0.90
    
     ;with CTE as
     (
      select *
     ,ROW_NUMBER()over(partition by ownerid order by startdate)rn 
      from #temp 
      ),
      CTE1 as
      (
      select ownerid
      ,startdate,enddate,Volume
      ,Cast((c.Volume+@ExistingVolume) as int)  CalulatedVolume
      ,Cast((c.Volume+@ExistingVolume) as int)-c.Volume as AppliedExistingVolume
      ,cast((c.Volume+@ExistingVolume-
      cast((c.Volume+@ExistingVolume) as int) )as decimal(5,2)) as RemainExistingVolume
     ,rn
      from CTE C where rn=1 
    
      union all
    
      select c.ownerid
      ,c.startdate,c.enddate,c.Volume
      ,cast((c.Volume+c1.RemainExistingVolume) as int) as CalulatedVolume
       ,cast((c.Volume+c1.RemainExistingVolume) as int)-c.Volume as AppliedExistingVolume
      ,cast(((c.Volume+c1.RemainExistingVolume)
      -cast((c.Volume+c1.RemainExistingVolume) as int)) as decimal(5,2))
      ,c.rn
      from CTE C 
      inner join CTE1 C1 on c.OwnerID=c1.OwnerID
      where c.rn=c1.rn+1 
      )
    
    
    
      select ownerid, 
      startdate,enddate
      ,CalulatedVolume
      ,case when AppliedExistingVolume<0 then 0 
      else AppliedExistingVolume end as AppliedExistingVolume
      ,RemainExistingVolume
      from CTE1
    
    
     drop table #temp
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 1970-01-01
      • 2020-05-03
      • 2016-11-14
      • 2011-04-15
      • 1970-01-01
      相关资源
      最近更新 更多