SQL Server Date Diff On Rows In Table - sql

I have a query that groups a start and stop time on different rows, where Batch Number 1 is the start, and BatchNumber 2 is the stop time, and I need to do a datediff on them. I have tried adding row numbers and trying to do something like date diff rows 1 and 2, 3 and 4, etc, with no luck.
So I have to do a date diff on 1 and 3, 2 and 4, and so on whenever there is batch 1 and 2 together.
Here is what my data looks like:
RowNumber OrderNumber IDCode DateVal MilestoneID BatchNumber
-------------------------------------------------------------------
1 5017555.1 4077213 2018-08-30 12:22:51.253 15 1
3 5017555.1 4081502 2018-09-05 12:41:08.817 16 2
2 5017555.1 4095474 2018-09-18 10:42:47.457 15 1
4 5017555.1 4095665 2018-09-18 12:07:11.083 16 2

LAG allows you to get value from a previous row:
Select *
datediff (day, LAG(DateVal) OVER (ORDER BY OrderNumber) ,DateVal )
From YourTable
You can also use Lead to get value from next row

Related

Snowflake SQL: trying to calculate time difference between subsets of subsequent rows

I have some data like the following in a Snowflake database
DEVICE_SERIAL
REASON_CODE
VERSION
MESSAGE_CREATED_AT
NEXT_REASON_CODE
BA1254862158
1
4
2022-06-23 02:06:03
4
BA1254862158
4
4
2022-06-23 02:07:07
1
BA1110001111
1
5
2022-06-16 16:19:04
4
BA1110001111
4
5
2022-06-16 17:43:04
1
BA1110001111
5
5
2022-06-20 14:37:45
4
BA1110001111
4
5
2022-06-20 17:31:12
1
that's the result of a previous query. I'm trying to get the difference between message_created_at timestamps where the device_serial is the same between subsequent rows, and the first row (of the pair for the difference) has reason_code of 1 or 5, and the second row of the pair has reason_code 4.
For this example, my desired output would be
DEVICE_SERIAL
VERSION
DELTA_SECONDS
BA1254862158
4
64
BA1110001111
5
5040
BA1110001111
5
10407
It's easy to calculate the time difference between every pair of rows (just lead or lag + datediff). But I'm not sure how to structure a query to select only the desired rows so that I can get a datediff between them, without calculating spurious datediffs.
My ultimate goal is to see how these datediffs change between versions. I am but a lowly C programmer, my SQL-fu is weak.
with data as (
select *,
count(case when reason_code in (1, 5) then 1 end)
over (partition by device_serial order by message_created_at) as grp
/* or alternately bracket by the end code */
-- count(case when reason_code = 4 then 1 end)
-- over (partition by device_serial order by message_created_at desc) as grp
from T
)
select device_serial, min(version) as version,
datediff(second, min(message_created_at), max(message_created_at)) as delta_seconds
from data
group by device_serial, grp

How to get SQL output with more rows than original table? - Timeline of Events

I have a table as follows:
event_num occurs_at_time length
1 0 10
2 10 3
3 20 10
4 30 5
Intended output:
start_time length event_type
0 10 Occurrence
10 3 Occurrence
13 7 Free Time
20 10 Occurrence
30 5 Occurrence
I'm having a hard time figuring out how to create a new row for Free Time in a SELECT statement. Free Time events occur whenever the difference between the next row occurs_at_time and previous row length + occurs_at_time is > 0.
For instance, between event_num 2 and event_num 3, 20 - 10 - 3 = 7 is the length and 10 + 3 = 13 will be the start_time.
I tried using LAG() and LEAD() window functions with CASE WHEN clauses to compare the next and previous rows, but I'm not sure how I can create a new row in the middle.
Use below
select * from (
select occurs_at_time as start_time,
length,
'Occurrence' as event_type
from your_table
union all
select occurs_at_time + length as start_time,
lead(occurs_at_time) over(order by occurs_at_time) - occurs_at_time - length as length,
'Free Time' event_type
from your_table
)
where length > 0
if applied to sample data in your question - output is

SQL Get max value of n next rows

Say I have a table with two columns: the time and the value. I want to be able to get a table with :
for each time get the max values of every next n seconds.
If I want the max value of every next 3 seconds, the following table:
time
value
1
6
2
1
3
4
4
2
5
5
6
1
7
1
8
3
9
7
Should return:
time
value
max
1
6
6
2
1
4
3
4
5
4
2
5
5
5
5
6
1
3
7
1
7
8
3
NULL
9
7
NULL
Is there a way to do this directly with an sql query?
You can use the max window function:
select *,
case
when row_number() over(order by time desc) > 2 then
max(value) over(order by time rows between current row and 2 following)
end as max
from table_name;
Fiddle
The case expression checks that there are more than 2 rows after the current row to calculate the max, otherwise null is returned (for the last 2 rows ordered by time).
Similar Version to Zakaria, but this solution uses about 40% less CPU resources (scaled to 3M rows for benchmark) as the window functions both use the same exact OVER clause so SQL can better optimize the query.
Optimized Max Value of Rolling Window of 3 Rows
SELECT *,
MaxValueIn3SecondWindow = CASE
/*Check 3 rows exists to compare. If 3 rows exists, then calculate max value*/
WHEN 3 = COUNT(*) OVER (ORDER BY [Time] ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
/*Returns max [Value] between the current row and the next 2 rows*/
THEN MAX(A.[Value]) OVER (ORDER BY [Time] ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
END
FROM #YourTable AS A

How to create two new columns based on previous rows in SQL Server?

Suppose I have this table(sort by the Date):
Hours Amount Date
1 2 20 1
2 1 20 3
3 6 20 10
4 3 20 20
And I want to create two new columns. Something like this
Hours Amount Start End Time
1 2 20 20 18 1
2 1 20 18 17 3
3 6 20 17 11 10
4 3 20 11 7 20
Start:
the first Start is the first Amount,
the next one is based on the first Amount - Hours
and so on
End is basically the next row for Start
Is there a way to do this?
You can use a running sum to do this. Then a lag to get the previous end on to the current row.
select t.*,coalesce(lag(end) over(order by date),start) as start
from (select hours,amount,date,amount-sum(hours) over(order by date) as end
from tbl
) t
Simply subtract the Running Total of those hours from the Amount:
select t.*
,amount - cumulative_hours as end
,amount - cumulative_hours + hours as start
from
(
select hours
,amount
,date
,sum(hours) over(order by date rows unbounded preceding) as cumulative_hours
from tab
) t

Assign value to a column based on values of other columns in the same table

I have a table with columns Date and Order. I want to add a column named Batch to this table which will be filled as follows: For each Date, we start from the first Order, and group each two orders in one batch.
It means that for records with Date = 1 in this example (the first 4 records), the first two records (Order= 10 and Order=30) will have batch number: Batch = 1, the next two records (Order = 80 and Order = 110) will have Batch = 2, and so on.
If at the end the number of remaining record(s) is less than the batch size (2 in this example),
the remained order(s) will have a separate Batch number, as in the example below, number of records with Date=2 is odd, so the last record (5th records) will have Batch = 3.
Date Order
-----------
1 10
1 30
1 80
1 110
2 20
2 30
2 50
2 70
2 120
3 90
Date Order Batch
------------------
1 10 1
1 30 1
1 80 2
1 110 2
2 20 1
2 30 1
2 50 2
2 70 2
2 120 3
3 90 1
Use the analytic function row_number to get row numbers 1,2,3,... within each date. Then add one and divide by two:
select
dateid,
orderid,
trunc((row_number() over (partition by dateid order by orderid) +1 ) / 2) as batch
from mytable;