Как дублировать строки в SQL на основе разницы между столбцами даты и разделенным агрегированным столбцом на повторяющуюся строку? - PullRequest
0 голосов
/ 22 декабря 2018

У меня есть таблица с некоторыми записями о расходе топлива.Важными столбцами в таблице являются: 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

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Используя структуру connect by level с учетом to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') в качестве месяца, я смог решить, как показано ниже:

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;

Я столкнулся с интересной проблемой: если яРассмотрим условие соединения в подзапросе, так как запрос c.ID >= 1 зависает в течение огромного промежутка времени, поэтому он разбивается на две части на union all как c.ID >= 2 и c.ID = 1

Rextester Demo

0 голосов
/ 22 декабря 2018

Ваше бизнес-правило рассматривает разницу между CONSUME_DATE_FROM и CONSUM_DATE_TO как абсолютные месяцы.Таким образом, вы ожидаете, что разница между 2018-10-25 и 2018-12-01 составит три месяца, тогда как разница в днях фактически равна примерно 1,1 месяцам.Таким образом, мы не можем использовать простую арифметику дат для получения желаемого результата, нам нужно выполнить дополнительное массирование дат.

В приведенном ниже запросе реализована желаемая логика путем получения первого дня месяца для * 1007.* и последний день месяца для CONSUME_DATE_TO, затем используйте ceil() для округления разницы до ближайшего целого числа месяцев.

Это вычисляется в подзапросе, который используется в основном запросе со старым трюком connect by level для умножения записи на level количество раз:

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_RELOAD_GAS », который отображает дату разницы для похожих строк» ​​

Из вашего опубликованного примера кажется, что DATE_RELOAD_GAS - первый день месяца для каждого месяца, ограниченныйCONSUME_DATE_FROM и CONSUM_DATE_TO.Я исправил свое решение для реализации этого правила.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...