| RecordId | high_speed | speed | DateFrom | DateTo |
---------------------------------------------------------------
| 666542 | 60 | 10 | 09/11/2011 | 10/11/2011 |
| 666986 | 20 | 20 | 11/11/2011 | 11/11/2011 |
| 666996 | 0 | 0 | 13/11/2011 | 17/11/2011 |
| 755485 | 0 | 0 | 01/11/2011 | 14/11/2011 |
| 758545 | 70 | 50 | 15/11/2011 | 26/11/2011 |
| 796956 | 40 | 40 | 09/11/2011 | 09/11/2011 |
| 799656 | 25 | 20 | 09/11/2011 | 09/11/2011 |
| 808845 | 0 | 0 | 15/11/2011 | 15/11/2011 |
| 823323 | 0 | 0 | 15/11/2011 | 16/11/2011 |
| 823669 | 0 | 0 | 17/11/2011 | 18/11/2011 |
| 899555 | 0 | 0 | 18/11/2011 | 19/11/2011 |
| 990990 | 20 | 10 | 12/11/2011 | 12/11/2011 |
Here, I want to construct database view which combines the consecutive rows having speed = 0. In that case, DateFrom will be the DateFrom value from first row & DateTo will be the DateTo value of last row. Which results into table as follows:
| high_speed | speed | DateFrom | DateTo |
---------------------------------------------------
| 60 | 10 | 09/11/2011 | 10/11/2011 |
| 20 | 20 | 11/11/2011 | 11/11/2011 |
| 0 | 0 | 13/11/2011 | 14/11/2011 |
| 70 | 50 | 15/11/2011 | 26/11/2011 |
| 40 | 40 | 09/11/2011 | 09/11/2011 |
| 25 | 20 | 09/11/2011 | 09/11/2011 |
| 0 | 0 | 15/11/2011 | 19/11/2011 |
| 20 | 10 | 12/11/2011 | 12/11/2011 |
Is there any possible way to get result in database view or function?
Note - 1. Removed devID column. It was very confusing instead of it added another column for understanding of the question. 2. Also additionally, I need to add one "Period" column i.e function which is difference of "DateFrom" & "DateTo" column.
This query using analytic functions lag(), lead() and some logic with case ... when gives desired output:
select high_speed, speed, datefrom, dateto, dateto-datefrom period
from (
select recordid, high_speed, speed, datefrom,
case when tmp = 2 then lead(dateto) over (order by recordid)
else dateto end dateto, tmp
from (
select test.*, case when speed <> 0 then 1
when lag(speed) over (order by recordid) <> 0 then 2
when lead(speed) over (order by recordid) <> 0 then 3
end tmp
from test )
where tmp is not null)
where tmp in (1, 2) order by recordid
SQLFiddle
This is another approach for the same solution. It uses lag(), lead() and partition by
The difference to the previous solution is that this query binds rows by consecutive periods minding the gaps, i.e:
Consider all rows with speed 0.
Result:
Also note that periods that share the same days like 15/11-15/11 and 13/11-17/11 will break this query. The sample data provided has periods like this.
-- for better understanding, start reading from the most nested query to the outer
-- QUERY 4: Removes duplicates
-- this query removes duplicates, because both border-rows on a multiple-row period will be identical
-- after the query 3
select distinct
high_speed,
speed,
datefrom,
dateto,
dateto-datefrom period
from
(
-- QUERY 3: Selects border-rows and builds datefrom and dateto.
-- this query selects all border-rows, which have the datefrom and dateto data that we need
-- to build the bigger period row.
--
-- this query also builds the bigger period datefrom and dateto
select
high_speed,
speed,
CASE WHEN is_previous_a_border = 0 and is_next_a_border = 1 then lag(datefrom) over (partition by speed order by datefrom)
WHEN is_previous_a_border = 1 and is_next_a_border = 0 then datefrom
WHEN is_previous_a_border = 1 and is_next_a_border = 1 then datefrom
ELSE null END datefrom,
CASE WHEN is_previous_a_border = 0 and is_next_a_border = 1 then dateto
WHEN is_previous_a_border = 1 and is_next_a_border = 0 then lead(dateto) over (partition by speed order by datefrom)
WHEN is_previous_a_border = 1 and is_next_a_border = 1 then dateto
ELSE null END dateto
from (
-- QUERY 2: Create syntax-sugar
-- this query creates some syntax-sugar properties:
-- - "is_previous_a_border": defines if previous row is a border
-- - "is_next_a_border": defines if previous row is a border
select
high_speed,
speed,
datefrom,
dateto,
is_border,
nvl(lag(is_border) over (partition by speed order by datefrom), 1) as is_previous_a_border,
nvl(lead(is_border) over (partition by speed order by datefrom), 1) as is_next_a_border
from (
-- QUERY 1: Create "is_border" property
-- this query creates the "is_border" property, which defines if a row is a border of a bigger multiple-row period
-- we use partition by to group rows and lag/lead to flag rows with consecutive periods
--
-- note that both border-rows of a bigger multiple-row period will have is_border = 1, while all rows in between
-- them, will have is_border = 0.
select
high_speed,
speed,
datefrom,
dateto,
case when lead(datefrom) over (partition by speed order by datefrom) between datefrom and dateto + interval '1' day
and lag(dateto) over (partition by speed order by datefrom) between datefrom - interval '1' day and dateto then 0
else 1 end is_border
from
test))
where is_border = 1)
order by
speed, datefrom;
SQL Fiddle (with altered sample data)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With