Home > Net >  How to subtract excessive hours from one row and add those hours to the next row using SQL
How to subtract excessive hours from one row and add those hours to the next row using SQL

Time:11-13

I'm trying to subtract extra hours from a row when the total hours > 10 and add the subtracted hours to the next rows. For example, the data table looks like this:

ID            StartTime                EndTime            Type   Rate   Location
123    2016-03-03 22:00:00.000  2016-03-04 10:00:00.000    B     2.5      VA
123    2016-03-04 10:00:00.000  2016-03-04 20:00:00.000    A     2.5      VA

As long as the IDs are the same, if the total hours > 10 and the type is B, I'd like to subtract the extra hours and add it to the next row. Other column values are just the same. For example, since the first row has 12 hours and the type is B, I want to subtract 2 hours from the first row and add the two hours to the next row. So the final data will look like this:

ID            StartTime                EndTime            Type   Rate   Location
123    2016-03-03 22:00:00.000  2016-03-04 08:00:00.000    B     2.5      VA
123    2016-03-04 08:00:00.000  2016-03-04 20:00:00.000    A     2.5      VA

You can see the EndTime of the first row and the StartTime of the second row have been adjusted. I tried this but couldn't really figure it out.

select 
   case when Type = 'B' and DATEDIFF(HOUR, StartTime , EndTime) > 10
   then DATEADD(HOUR, DATEDIFF(HOUR, StartTime , EndTime) ,EndTime) else EndTime as EndTime,

I believe the current code is not accurate and couldn't really figure out how to add the subtracted hours to the next row.

Thank you.

CodePudding user response:

This is fairly trivial - if a little ugly - with the help of the lag window function, which can look back within a defined window of data and use the values held in other rows.

Given the ambiguity in what your actual logic is, I have added two versions of the calc required, for which you can see the different output against ID = 124.

Query

declare @t table(ID int,StartTime datetime,EndTime datetime,[Type] varchar(10),Rate decimal(10,2),[Location] varchar(10));
insert into @t values
 (123,'2016-03-03 22:00:00.000','2016-03-04 10:00:00.000','B',2.5,'VA')
,(123,'2016-03-04 10:00:00.000','2016-03-04 20:00:00.000','A',2.5,'VA')
,(124,'2016-03-03 01:00:00.000','2016-03-03 12:00:00.000','C',2.5,'VA')
,(124,'2016-03-03 12:00:00.000','2016-03-04 10:00:00.000','B',2.5,'VA')
,(124,'2016-03-04 11:00:00.000','2016-03-04 11:00:00.000','A',2.5,'VA')
,(125,'2016-03-02 12:00:00.000','2016-03-03 10:00:00.000','A',2.5,'VA')
,(125,'2016-03-03 10:00:00.000','2016-03-03 11:00:00.000','A',2.5,'VA')
,(126,'2016-03-03 08:00:00.000','2016-03-03 10:00:00.000','B',2.5,'VA')
,(126,'2016-03-03 10:00:00.000','2016-03-03 11:00:00.000','A',2.5,'VA')
;

select ID

      ,case when lag(Type) over (partition by ID order by StartTime) = 'B'
             and datediff(minute
                         ,lag(StartTime) over (partition by ID order by StartTime)
                         ,lag(EndTime) over (partition by ID order by StartTime)
                         ) >= 600
            then dateadd(hour,10,lag(StartTime) over (partition by ID order by StartTime))
            else StartTime
            end as StartTimeContiguous

      ,case when lag(Type) over (partition by ID order by StartTime) = 'B'
             and datediff(minute
                         ,lag(StartTime) over (partition by ID order by StartTime)
                         ,lag(EndTime) over (partition by ID order by StartTime)
                         ) >= 600
            then dateadd(minute
                        ,600 - datediff(minute
                                       ,lag(StartTime) over (partition by ID order by StartTime)
                                       ,lag(EndTime) over (partition by ID order by StartTime)
                                       )
                        ,StartTime
                        )
            else StartTime
            end as StartTimeSubtraction

      ,case when Type = 'B' and datediff(minute,StartTime,EndTime) >= 600
            then dateadd(hour,10,StartTime)
            else EndTime
            end as EndTime
      ,[Type]
      ,Rate
      ,[Location]
from @t
order by ID
        ,StartTime;

Output

ID StartTimeContiguous StartTimeSubtraction EndTime Type Rate Location
123 2016-03-03 22:00:00.000 2016-03-03 22:00:00.000 2016-03-04 08:00:00.000 B 2.50 VA
123 2016-03-04 08:00:00.000 2016-03-04 08:00:00.000 2016-03-04 20:00:00.000 A 2.50 VA
124 2016-03-03 01:00:00.000 2016-03-03 01:00:00.000 2016-03-03 12:00:00.000 C 2.50 VA
124 2016-03-03 12:00:00.000 2016-03-03 12:00:00.000 2016-03-03 22:00:00.000 B 2.50 VA
124 2016-03-03 22:00:00.000 2016-03-03 23:00:00.000 2016-03-04 11:00:00.000 A 2.50 VA
125 2016-03-02 12:00:00.000 2016-03-02 12:00:00.000 2016-03-03 10:00:00.000 A 2.50 VA
125 2016-03-03 10:00:00.000 2016-03-03 10:00:00.000 2016-03-03 11:00:00.000 A 2.50 VA
126 2016-03-03 08:00:00.000 2016-03-03 08:00:00.000 2016-03-03 10:00:00.000 B 2.50 VA
126 2016-03-03 10:00:00.000 2016-03-03 10:00:00.000 2016-03-03 11:00:00.000 A 2.50 VA

CodePudding user response:

If I have not missunderstood you, this would be what you want:

with cte 
as
(
  select 
  row_number() over (order by ID, Type desc) rn,
  *,
  datediff(s, StartTime, EndTime) - (60 * 60 * 10) DiffSeconds
  from MyTable
),
final 
as
(
select 
  *,
  sum(DiffSeconds) over (PARTITION BY ID ORDER BY ID, Type DESC) SumDiffSeconds 
  from cte
)
select 
  ID,
  case
    when SumDiffSeconds > 0 and Type != 'B' then dateadd(s, -SumDiffSeconds, StartTime) 
    else StartTime 
  end NewStartTime,
  case
    when SumDiffSeconds > 0 and Type = 'B' then dateadd(s, -SumDiffSeconds, EndTime) 
    else EndTime 
  end NewEndTime,
  Type, Rate, Location
from final

If it is not, please provide more sample data and expected results, and I will adjust the select.

DbFiddle: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=cb511db70f8822e1e184aa1a3cceca01

Results are:

ID NewStartTime NewEndTime Type Rate Location
123 2016-03-03 22:00:00.000 2016-03-04 08:00:00.000 B 2.5 VA
123 2016-03-04 08:00:00.000 2016-03-04 20:00:00.000 A 2.5 VA
  • Related