如何在SQL中根据日期列和每个重复行划分的聚合列之间的差异来复制行?

如何在SQL中根据日期列和每个重复行划分的聚合列之间的差异来复制行?,sql,oracle,Sql,Oracle,我有一张关于油耗的记录表。表中的重要列是:消费日期从和消费日期到 我想每月计算每辆车的平均燃油消耗量,但有些行不在同一个月内。例如,其中一些有三个月的差异,每公升的总汽油量汇总在一行中 现在,我应该找到在CONSUME_DATE_FROM和CONSUM_DATE_TO之间相差超过一个月的记录,并在当前或第二个表中每个月的计数中复制它们,并在相关行之间除以每升总气体 我在该表中列出了以下数据: ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO

我有一张关于油耗的记录表。表中的重要列是:消费日期从和消费日期到

我想每月计算每辆车的平均燃油消耗量,但有些行不在同一个月内。例如,其中一些有三个月的差异,每公升的总汽油量汇总在一行中

现在,我应该找到在CONSUME_DATE_FROM和CONSUM_DATE_TO之间相差超过一个月的记录,并在当前或第二个表中每个月的计数中复制它们,并在相关行之间除以每升总气体

我在该表中列出了以下数据:

ID    VehicleId CONSUME_DATE_FROM  CONSUM_DATE_TO   GAS_PER_LITER
1      100      2018-10-25         2018-12-01           600         
2      101      2018-07-19         2018-07-24           100 
3      102      2018-12-31         2019-01-01           400
4      103      2018-03-29         2018-05-29           200
5      104      2018-02-05         2018-02-09           50
预期输出表应如下所示

ID    VehicleId CONSUME_DATE_FROM  CONSUM_DATE_TO   GAS_PER_LITER
1      100      2018-10-25          2018-12-01      200         
1      100      2018-10-25          2018-12-01      200     
1      100      2018-10-25          2018-12-01      200     
2      101      2018-07-19          2018-07-24      100 
3      102      2018-12-31          2019-01-01      200
3      102      2018-12-31          2019-01-01      200
4      103      2018-03-29          2018-05-29      66.66
4      103      2018-03-29          2018-05-29      66.66
4      103      2018-03-29          2018-05-29      66.66
5      104      2018-02-05          2018-02-09      50
或如下

ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO  GAS_PER_LITER DATE_RELOAD_GAS        
    1      100      2018-10-25       2018-12-01     200        2018-10-01       
    1      100      2018-10-25       2018-12-01     200        2018-11-01           
    1      100      2018-10-25       2018-12-01     200        2018-12-01           
    2      101      2018-07-19       2018-07-24     100        2018-07-01
    3      102      2018-12-31       2019-01-01     200        2018-12-01
    3      102      2018-12-31       2019-01-01     200        2019-01-01
    4      103      2018-03-29       2018-05-29     66.66      2018-03-01
    4      103      2018-03-29       2018-05-29     66.66      2018-04-01
    4      103      2018-03-29       2018-05-29     66.66      2018-05-01
    5      104      2018-02-05       2018-02-09     50         2018-02-01
有人能帮我解答这个问题吗


我使用的是oracle数据库

您的业务规则将消费日期与消费日期之间的差异视为绝对月份。因此,您预计2018-10-25和2018-12-01之间的差异为三个月,而天数的差异实际上相当于约1.1个月。因此,我们不能使用简单的日期算法来获得所需的输出,我们需要对日期进行一些额外的处理

下面的查询实现了您所需的逻辑,方法是导出CONSUME_DATE_FROM的月份第一天和CONSUME_DATE_TO的月份最后一天,然后使用ceil将差值四舍五入到最接近的月份整数

这是在主查询中使用的子查询中计算的,该子查询使用旧的“按级别连接”技巧将记录乘以级别的次数:

with cte as (
    select f.*
          , ceil(months_between(last_day(CONSUM_DATE_TO)
                                , trunc(CONSUME_DATE_FROM,'mm'))) as diff 
    from fuel_consumption f
)
select cte.id
       , cte.VehicleId
       , cte.CONSUME_DATE_FROM
       , cte.CONSUM_DATE_TO 
       , cte.GAS_PER_LITER/cte.diff as GAS_PER_LITER
       , add_months(trunc(cte.CONSUME_DATE_FROM, 'mm'), level-1) as DATE_RELOAD_GAS
from cte
connect by level <= cte.diff
and prior cte.id = cte.id
and prior sys_guid() is not null
; 
如果添加一个额外的列DATE\u RELOAD\u GAS来显示相似行的差异日期,会怎么样

从您发布的样本来看,似乎DATE_RELOAD_GAS是每个月的第一天,以CONSUME_DATE_From和CONSUM_DATE_TO为界。我已修改了我的解决方案以实现此规则。

通过使用“按级别连接”结构,并考虑从+level-1、'yyyymm'开始使用日期,我可以解决以下问题:

我遇到了一个有趣的问题,如果我把子查询中的连接条件看作C.ID>=1,查询会在很长一段时间内挂起,所以通过联合所有的方法分成两个部分。 当c.ID>=2和c.ID=1时


为什么有些记录会被复制,而另一些记录会被复制成三份?我不明白你的逻辑。DATE\u RELOAD\u GAS从哪里来?它不在你的初始数据中???不,它不是我的初始数据。这只是计算后的一个新列。谢谢,它工作正常,但如果添加一个额外的列DATE_RELOAD_GAS,显示类似行的差异日期,该怎么办?谢谢让我检查一下谢谢,它工作正常,但是如果添加一个额外的列DATE\u RELOAD\u GAS来显示相似数据的差异日期呢rows@GhostMan不客气。在检测到myMonth后,它很简单。刚把我的月分成了两部分
select ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO, 
           trunc(GAS_PER_LITER/max(rn) over (partition by ID order by ID),2) as GAS_PER_LITER, 
          '01.'||substr(myMonth,5,2)||'.'||substr(myMonth,1,4) as DATE_RELOAD_GAS      
      from
      (
      with consumption( ID, VehicleId, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER ) as
      (
       select 1,100,date'2018-10-25',date'2018-12-01',600 from dual union all         
       select 2,101,date'2018-07-19',date'2018-07-24',100 from dual union all          
       select 3,102,date'2018-12-31',date'2019-01-01',400 from dual union all         
       select 4,103,date'2018-03-29',date'2018-05-29',200 from dual union all         
       select 5,104,date'2018-02-05',date'2018-02-09', 50 from dual        
      )
       select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth, 
              VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
              row_number() over (partition by ID order by ID) as rn
         from dual join consumption c 
           on c.ID >= 2
      group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
               c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
      connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
      union all
       select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth,
              VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
              row_number() over (partition by ID order by ID) as rn
         from dual join consumption c 
           on c.ID  = 1
      group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
               c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
      connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
      ) q
    group by ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER, rn  
    order by ID, myMonth;