Does anyone know of a way to LAG in a specified order? In the example below, I provide a table with my current output [Lag (Current)] and my desired output [Lag (Desired)] columns. I am interested in creating a lag for the event time based on event time order, need to make sure that the Groups do not get out of order. The issue is that Group CL1 is currently not being lagged based on time, its based on the group, so that is where I am struggling.
Personal ID
Event Time
Groups
Lag (Current)
Lag (Desired)
99999999
4:18:58 PM
GM1
99999999
4:21:03 PM
GM1
4:18:58 PM
4:18:58 PM
99999999
4:21:42 PM
CL1
99999999
4:25:04 PM
CL1
4:21:42 PM
4:21:42 PM
99999999
4:25:40 PM
GM2
99999999
4:25:45 PM
GM2
4:25:40 PM
4:25:40 PM
99999999
4:26:14 PM
CL1
4:25:04 PM
99999999
4:26:23 PM
CL1
4:21:42 PM
4:21:42 PM
Here is the SQL I have tried.
SELECT LAG() OVER (PARTITION BY Personal ID, Groups ORDER BY Personal ID, Event Time)
Updated
Second example
The below seemed to work, but not sure if its correct, especially as more data passes through. It did work for this example and the one you provided above, I just added the groups in the the last w2.
WITH sample_table AS (
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'CL1' groups UNION ALL
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'FZ1' UNION ALL
SELECT '99999999' personal_id, '8:13:42 AM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '8:13:42 AM' event_time, 'GM2'
)
Expected Output
Consider below gaps and islands approach.
WITH sample_table AS (
SELECT '99999999' personal_id, '4:18:58 PM' event_time, 'GM1' groups UNION ALL
SELECT '99999999' personal_id, '4:21:03 PM' event_time, 'GM1' UNION ALL
SELECT '99999999' personal_id, '4:21:42 PM' event_time, 'CL1' UNION ALL
SELECT '99999999' personal_id, '4:25:04 PM' event_time, 'CL1' UNION ALL
SELECT '99999999' personal_id, '4:25:40 PM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '4:25:45 PM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '4:26:14 PM' event_time, 'CL1' UNION ALL
SELECT '99999999' personal_id, '4:26:23 PM' event_time, 'CL1'
)
SELECT personal_id, event_time, groups, LAG(event_time) OVER w2 AS lag FROM (
SELECT *, COUNT(1) OVER w1 - SUM(IF(flag, 1, 0)) OVER w1 AS part FROM (
SELECT *, groups = LAG(groups) OVER w0 AS flag
FROM sample_table
WINDOW w0 AS (PARTITION BY personal_id ORDER BY event_time)
) t WINDOW w1 AS (PARTITION BY personal_id ORDER BY event_time)
) t WINDOW w2 AS (PARTITION BY personal_id, part ORDER BY event_time);
Query results
Updated
WITH sample_table AS (
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'CL1' groups UNION ALL
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '1:04:12 AM' event_time, 'FZ1' UNION ALL
SELECT '99999999' personal_id, '8:13:42 AM' event_time, 'GM2' UNION ALL
SELECT '99999999' personal_id, '8:13:43 AM' event_time, 'GM2'
)
SELECT personal_id, event_time, groups, LAG(event_time) OVER w2 AS lag FROM (
SELECT *, COUNT(1) OVER w1 - SUM(IF(flag, 1, 0)) OVER w1 AS part FROM (
SELECT *, groups = LAG(groups) OVER w0 AS flag
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY personal_id, event_time) AS rn FROM sample_table) t
WINDOW w0 AS (PARTITION BY personal_id ORDER BY event_time, rn)
) t WINDOW w1 AS (PARTITION BY personal_id ORDER BY event_time, rn)
) t WINDOW w2 AS (PARTITION BY personal_id, part ORDER BY event_time, rn);
Query results
Related
I have a hospital bed census that is triggered and creates a date/time stamped row in a table. when the bed check portion is done it labels the event census. i have found that some patients on days they were in the hospital have not been timestamped with the event census. I am trying to write a query to capture all patients that may have had this issue.
i need to capture the patients between their admit and discharge dates, and then any day they do not have a time stamp event of census. for example, this patient does not have a census on the 12th or 13th but does on the 14th. i want to be able to pull this pat_id and dates they are not stamped with census.
11-APR-2019 11:59:00 PM CENSUS
12-APR-2019 03:12:00 PM TRANSFER OUT
12-APR-2019 03:12:00 PM TRANSFER IN
14-APR-2019 07:06:00 AM PATIENT UPDATE
14-APR-2019 11:40:00 AM TRANSFER OUT
14-APR-2019 11:40:00 AM TRANSFER IN
14-APR-2019 11:59:00 PM CENSUS
I created a calendar portion to my query. then i created a query to capture patients in a time frame. from there i am a bit stuck.
DATE1
AS
(select
to_char(dates,'MM/DD/YYYY') AS WEEK_DATE,
dates,
to_char(dates,'D') weekday,
to_char(dates,'mm') m_onth,
to_char(dates,'ww') week_of_year,
to_char(dates,'dd') month_day,
to_char(dates,'ddd') Year_day,
SUBSTR(dates,1,2) AS WEEKDATE
from (SELECT TRUNC(to_date(v.yyyy,'YYYY'),'YY') +LEVEL - 1 DATES
FROM ( SELECT 2019 yyyy FROM dual ) v
CONNECT BY LEVEL < 366
)
)
,
ADT
AS (select distinct
adt.pat_id,
peh.y_mrn,
adt.DEPARTMENT_ID,
adp.department_name,
--peh.HOSP_ADMSN_TIME,
to_char(peh.HOSP_ADMSN_TIME,'MM/DD/YYYY') AS HOSP_ADMSN_TIME2,
--peh.HOSP_DISCH_TIME,
to_char(peh.HOSP_DISCH_TIME,'MM/DD/YYYY') AS HOSP_DISCH_TIME2,
adt.effective_time,
to_char(aDT.effective_time,'MM/DD/YYYY') AS EFFECT_DATE,
--LEAD(adt.effective_time) over (partition by ADT.pat_id order by ADT.pat_id, adt.effective_time) AS NEXT_EFF_DATE,
--CASE WHEN adt.event_type_c =6 THEN adt.effective_time END AS CENSUS_DATE,
et.title as event_type,
adt.event_type_c,
peh.ADT_PAT_CLASS_C,
Adt.event_subtype_c--,
--LAG(adt.effective_time) over (partition by ADT.pat_id order by ADT.pat_id, adt.effective_time) AS PREV_EFF_DATE
from
clarity_adt adt
left OUTER join
pat_enc_hsp peh
on
peh.pat_enc_csn_id = adt.pat_enc_csn_id
left outer join
clarity_dep adp
on adt.department_id = adp.department_id
left OUTER join
zc_event_type et
on adt.event_type_c = et.event_type_c
where
adt.effective_time between '08-apr-2019' and '15-apr-2019'
order by adt.effective_time
)
,
ADT2
AS
(
SELECT-- DISTINCT
D.WEEK_DATE,
A.HOSP_ADMSN_TIME2,
A.EFFECT_DATE,
A.PAT_ID,
CASE WHEN D.WEEK_DATE IS NOT NULL AND A.EFFECT_DATE IS NULL AND A.event_type <> 'CENSUS' THEN 1
WHEN D.WEEK_DATE IS NOT NULL AND A.EFFECT_DATE IS NULL AND A.event_type IS NULL THEN 1
WHEN D.WEEK_DATE IS NOT NULL AND A.EFFECT_DATE IS NOT NULL AND A.event_type <> 'CENSUS' THEN 1 ELSE 0
END AS NO_ADT_INFO,
A.event_type,
A.HOSP_DISCH_TIME2
FROM
DATE2 D
LEFT OUTER JOIN
ADT A
ON
D.WEEK_DATE = A.EFFECT_DATE
ORDER BY
D.WEEK_DATE)
i would like to end up with the patient id, the day of the week they have no census, the hosp admission & discharge dates
PAT_ID WEEK_DATE EVENT_TYPE HOSP_ADMSN_TIME HOSP_DISCH_TIME
ABCDEF 4/12/2019 NO CENSUS 4/10/2019 4/19/2019
ABCDEF 4/13/2019 NO CENSUS 4/10/2019 4/19/2019
GHIJK 4/8/2019 NO CENSUS 4/2/2019 4/12/2019
GHIJK 4/11/2019 NO CENSUS 4/2/2019 4/12/2019
Here is sample data for two patients:
events(pat_id, event_date, event_type) as (
select 'ABCD', to_date('2019-04-11 23:59', 'yyyy-mm-dd hh24:mi'), 'CENSUS' from dual union all
select 'ABCD', to_date('2019-04-12 15:12', 'yyyy-mm-dd hh24:mi'), 'TRANSFER OUT' from dual union all
select 'ABCD', to_date('2019-04-12 15:12', 'yyyy-mm-dd hh24:mi'), 'TRANSFER IN' from dual union all
select 'ABCD', to_date('2019-04-14 07:06', 'yyyy-mm-dd hh24:mi'), 'PATIENT UPDATE' from dual union all
select 'ABCD', to_date('2019-04-14 11:40', 'yyyy-mm-dd hh24:mi'), 'TRANSFER OUT' from dual union all
select 'ABCD', to_date('2019-04-14 11:40', 'yyyy-mm-dd hh24:mi'), 'TRANSFER IN' from dual union all
select 'ABCD', to_date('2019-04-14 23:59', 'yyyy-mm-dd hh24:mi'), 'CENSUS' from dual union all
select 'GHIJ', to_date('2019-05-17 23:59', 'yyyy-mm-dd hh24:mi'), 'CENSUS' from dual union all
select 'GHIJ', to_date('2019-05-19 23:59', 'yyyy-mm-dd hh24:mi'), 'CENSUS' from dual ),
peh(pat_id, hosp_admsn_time, hosp_disch_time) as (
select 'ABCD', date '2019-04-11', date '2019-04-14' from dual union all
select 'GHIJ', date '2019-05-17', date '2019-05-20' from dual ),
You can create recursive query generating days for each patient and check if there is CENSUS event for each of these days:
with cte(pat_id, num, adm, dis) as (
select pat_id, 0, hosp_admsn_time, hosp_disch_time from peh
union all
select pat_id, num + 1, adm, dis from cte where num < dis - adm)
select pat_id, day, 'NO CENSUS' info, adm, dis
from (select pat_id, adm + num day, adm, dis from cte) d
where not exists (
select 1
from events
where pat_id = d.pat_id and trunc(event_date) = d.day and event_type = 'CENSUS')
order by pat_id, day;
Result:
PAT_ID DAY INFO ADM DIS
------ ----------- --------- ----------- -----------
ABCD 2019-04-12 NO CENSUS 2019-04-11 2019-04-14
ABCD 2019-04-13 NO CENSUS 2019-04-11 2019-04-14
GHIJ 2019-05-18 NO CENSUS 2019-05-17 2019-05-20
GHIJ 2019-05-20 NO CENSUS 2019-05-17 2019-05-20
dbfiddle demo
I am trying to find a total duration consume by a Group by calculating date difference in a following query
with event AS (
SELECT 9000 AS ID, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03/10 10:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03-10 11:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03/20 10:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03-20 10:05:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03/25 09:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03-25 10:15:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03/26 12:00:00','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9002 AS ID, TO_DATE('2017-03-26 14:30:27','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9002 AS ID, TO_DATE('2017-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS') AS
TIMESTAMP, 'END' AS EVENT FROM DUAL
)
select id, min(timestamp) as call_start_ts, max(timestamp) as call_end_ts,
max(timestamp) - min(timestamp) as duration
from event t
group by id
order by 1;
I have also configure the SQLFiddle
Please help me
EDIT
Expected Result will be like below
Use the LAG or LEAD analytic functions to get the next END event's time:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE event ( id, timestamp, event ) AS
SELECT 9000, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/10 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-10 11:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/20 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-20 10:05:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/25 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-25 10:15:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/26 12:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2017-03-26 14:30:27','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2017-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL;
Query 1:
SELECT id,
MIN( timestamp ) AS start_ts,
MAX( end_time ) AS end_ts,
SUM( end_time - timestamp ) AS duration
FROM (
SELECT id,
timestamp,
event,
LEAD( CASE event WHEN 'END' THEN timestamp END )
OVER ( PARTITION BY id ORDER BY timestamp ) AS end_time
FROM event
)
WHERE event = 'Start'
GROUP BY id
ORDER BY id
Results:
| ID | START_TS | END_TS | DURATION |
|------|----------------------|----------------------|--------------------|
| 9000 | 2018-03-01T09:00:00Z | 2018-03-25T09:00:00Z | 13.996527777777779 |
| 9001 | 2018-03-10T11:00:00Z | 2018-03-26T12:00:00Z | 11.03125 |
| 9002 | 2017-03-26T14:30:27Z | 2017-04-05T15:02:56Z | 10.02255787037037 |
I solved the problem in two steps. First i match records in the same interval then i sum up their duration.
http://sqlfiddle.com/#!4/73f48/83
SELECT
Id,
round(SUM(duration))
FROM
(
SELECT
t.id,
MIN (t2. TIMESTAMP) - t. TIMESTAMP AS duration
FROM
event t,
event t2
WHERE
t.Id = t2.Id
AND t2.Event = 'END'
AND t.Event = 'Start'
AND t2. TIMESTAMP > t. TIMESTAMP
GROUP BY
t. TIMESTAMP,
t.Id
)
GROUP BY
Id
select
id, round(sum(end_timestamp - start_timestamp),3) DURATION
from (
select
t.id,
t.timestamp START_TIMESTAMP,
case when LEAD(t.event,1) OVER (partition by id order by timestamp, event desc) = 'END'
then LEAD(t.timestamp,1) OVER (partition by id order by timestamp, event desc)
else null end as END_TIMESTAMP
from event t
)tt
where end_timestamp is not null
group by id
Solution to your problem:
WITH event AS (
SELECT 9000 AS ID, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03/10 10:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03-10 11:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03/20 10:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03-20 10:05:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9000 AS ID, TO_DATE('2018-03/25 09:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03-25 10:15:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9001 AS ID, TO_DATE('2018-03/26 12:00:00','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'END' AS EVENT FROM DUAL UNION ALL
SELECT 9002 AS ID, TO_DATE('2017-03-26 14:30:27','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'Start' AS EVENT FROM DUAL UNION ALL
SELECT 9002 AS ID, TO_DATE('2017-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS') AS TIMESTAMP, 'END' AS EVENT FROM DUAL
)
,rn_event AS
(
select event.*,ROW_NUMBER() OVER (Partition BY ID ORDER BY TimeSTAMP) AS rn from event
)
, diff_event AS
(
SELECT e.ID, f.TIMESTAMP AS Start_time, e.timestamp AS End_Time, e.TIMESTAMP - f.timestamp AS duration
FROM rn_event e
INNER JOIN rn_event f
ON f.id = e.id AND f.EVENT = 'Start' AND f.rn = e.rn - 1
)
SELECT ID,MIN(Start_Time) START_TS, MAX(END_TIME) END_TS, ROUND(SUM(Duration)) AS Duration
FROM diff_event
GROUP BY ID;
OUTPUT:
ID START_TS END_TS DURATION
9000 2018-03-01T09:00:00Z 2018-03-25T09:00:00Z 14
9001 2018-03-10T11:00:00Z 2018-03-26T12:00:00Z 11
9002 2017-03-26T14:30:27Z 2017-04-05T15:02:56Z 10
A demo for the above query:
http://sqlfiddle.com/#!4/73f48/87
Need to count the number of user being added every day, given a date range from date and to date, an e.g is shown below:
select
'2017-06-01' as myDate
, count(distinct user_id)
from tbl_stats
where date(dateTime)<='2017-06-01'
union all
select
'2017-06-02' as myDate
, count(distinct user_id)
from tbl_stats
where date(dateTime)<='2017-06-02'
The output would be like:
reportDate | count
------------+-------
2017-06-01 | 2467
2017-06-02 | 2470
So, I will just have fromDate and toDate and i would need date wise distinct user count in the table. I will not be using any procedures or loops.
SELECT DATE(ts.dateTime) AS reportDate
, COUNT(distinct ts.user_id) AS userCount
FROM tbl_stats AS ts
WHERE ts.dateTime >= #lowerBoundDate
AND ts.dateTime < TIMESTAMPADD('DAY', 1, #upperBoundDate)
GROUP BY DATE(ts.dateTime)
To get cumulative (distinct) users count per day, use following, replace custom dates given in following example with your start and end dates.
WITH test_data AS (
SELECT '2017-01-01'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-01'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-03'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-03'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 6::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 6::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 7::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 8::int as user_id
UNION
SELECT '2017-01-06'::date as event_date, 7::int as user_id
UNION
SELECT '2017-01-06'::date as event_date, 9::int as user_id
)
SELECT event_date,
COUNT(distinct user_id) AS distinct_user_per_day,
SUM(COUNT(distinct user_id)) OVER (ORDER BY event_date) AS cumulative_user_count
FROM test_data
WHERE event_date >= '2017-01-01'
AND
event_date <= '2017-01-06'
GROUP BY
event_date
The events table looks like
event_type value timestamp
2 2 06-06-2016 14:00:00
2 7 06-06-2016 13:00:00
2 2 06-06-2016 12:00:00
3 3 06-06-2016 14:00:00
3 9 06-06-2016 13:00:00
4 9 06-06-2016 13:00:00
My goal is to filter event types that occur more than twice and subtract most two recent values and shows BY event_type.
The end result would be
event_type value
2 -5
3 -6
I was able to get filter events occurred more than twice and order by event_type based on timestamp desc.
The difficult part for me is to subtract most two recent values and shows BY event_type.
DB / SQL experts , please help
You can use a query like this:
SELECT event_type, diff
FROM (
SELECT event_type, value, "timestamp", rn,
value - LEAD(value) OVER (PARTITION BY event_type
ORDER BY "timestamp" DESC) AS diff
FROM (
SELECT event_type, value, "timestamp",
COUNT(*) OVER (PARTITION BY event_type) AS cnt,
ROW_NUMBER() OVER (PARTITION BY event_type ORDER BY "timestamp" DESC) AS rn
FROM mytable) AS t
WHERE cnt >=2 AND rn <= 2 ) AS s
WHERE rn = 1
The innermost subquery uses:
Window function COUNT with PARTITION BY clause, so as to calculate the population of each event_type slice.
Window function ROW_NUMBER so as to get the two latest records within each event_type slice.
The mid-level query uses LEAD window function, so as to calculate the difference between the first and the second records. The outermost query simply returns this difference.
Demo here
This example only for Oracle.
Test data:
with t(event_type,
value,
timestamp) as
(select 2, 2, to_timestamp('06-06-2016 14:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual
union all
select 2, 7, to_timestamp('06-06-2016 13:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual
union all
select 2, 2, to_timestamp('06-06-2016 12:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual
union all
select 3, 3, to_timestamp('06-06-2016 14:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual
union all
select 3, 9, to_timestamp('06-06-2016 13:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual
union all
select 4, 9, to_timestamp('06-06-2016 13:00:00', 'mm-dd-yyyy hh24:mi:ss')
from dual)
Query:
select event_type,
max(value) keep(dense_rank first order by rn) - max(value) keep(dense_rank last order by rn) as value
from (select event_type,
row_number() over(partition by event_type order by timestamp desc) rn,
value
from t) t
where rn in (1, 2)
group by event_type
having count (*) >= 2
I'm new to the site, and I'm hoping you can help...
I'm trying to stamp the LegNumber from Table 2 into Table 1 based on the datetime of the record in Table1 falling between the datetime of the record(s) from Table 2.
In my example, the records in Table 1 with a datetime that falls between 4/5/16 4:02 AM and 4/7/16 6:53 AM should be stamped with LegNumber 1862410 (from Table 2) based on the datetimes of 4/5/16 8:14 AM thru 4/5/16 4:09 PM. Hopefully, I haven't made this inquiry too confusing.
At first we create to cte's and gather time intervals, then select to show output:
;WITH LegsRowNumbers AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY LegStartDate ASC) as rn
FROM Table2 t2
), GetLegsIntervals AS (
SELECT l1.Tractor,
--Here may be another format, check CAST and CONVERT article on MSDN
CONVERT(datetime,l1.LegStartDate, 120) as LegStartDate,
DATEADD(minute,-1,l2.LegStartDate) as LegEndDate,
l1.LegNumber
FROM LegsRowNumbers l1
LEFT JOIN LegsRowNumbers l2
ON l2.rn = l1.rn+1
)
SELECT t.Tractor,
t.TollExitDateTime,
g.LegNumber
FROM Table1 t
LEFT JOIN GetLegsIntervals g
ON t.TollExitDateTime between g.LegStartDate and g.LegEndDate
Output:
Tractor TollExitDateTime LegNumber
1404 2016-04-03 05:21 AM NULL
1404 2016-04-03 05:34 AM NULL
1404 2016-04-03 06:28 AM NULL
1404 2016-04-03 02:36 PM NULL
1404 2016-04-03 03:13 PM NULL
1404 2016-04-03 03:29 PM NULL
1404 2016-04-05 08:14 AM 1862410
1404 2016-04-05 08:26 AM 1862410
1404 2016-04-05 09:26 AM 1862410
1404 2016-04-05 03:15 PM 1862410
1404 2016-04-05 03:53 PM 1862410
1404 2016-04-05 04:09 PM 1862410
You can change last query to UPDATE:
UPDATE t
SET LegNumber = g.LegNumber
FROM Table1 t
LEFT JOIN GetLegsIntervals g
ON t.TollExitDateTime between g.LegStartDate and g.LegEndDate
If you are one SQL Server 2012+, you can use the LEAD to set the LegEndDate for each:
create table TABLE1(TRACTOR integer, TOLLEXITDATETIME datetime);
create table TABLE2(TRACTOR integer, LEGSTARTDATE datetime, LEGNUMBER integer);
insert into TABLE1
select '1404', '4/3/2016 5:21:00 AM' union all
select '1404', '4/5/2016 5:21:00 AM' union all
select '1404', '4/6/2016 5:21:00 AM'
;
insert into TABLE2
select '1404', '4/4/2016 3:54:00 AM', 1862405 union all
select '1404', '4/5/2016 4:02:00 AM', 1862410 union all
select '1404', '4/7/2016 6:53:00 AM', 1865901
;
with TEMP_TABLE2(TRACTOR, LEGSTARTDATE, LEGENDDATE, LEGNUMBER)
AS(
select
TRACTOR,
LEGSTARTDATE,
lead(LEGSTARTDATE) over (partition by TRACTOR order by LEGSTARTDATE) LEGENDDATE,
LEGNUMBER
from TABLE2
)
select
t1.TRACTOR,
t1.TOLLEXITDATETIME,
t2.LEGNUMBER
from TABLE1 t1
left outer join TEMP_TABLE2 t2
on t1.TRACTOR = t2.TRACTOR
and t1.TOLLEXITDATETIME between t2.LEGSTARTDATE and coalesce(t2.LEGENDDATE, '12/31/9999')
Thank you mo2 for starter table code :)
And thank you gofr1 for the
ROW_NUMBER() OVER (ORDER BY LegStartDate ASC) as rn because even though I use it all the time I completely blanked it.
Table I used for testing
create table Table2(Tractor integer, LegStartDate datetime, LegNumber integer primary key);
create table Table1(Tractor integer, TollExitDateTime datetime, LegNumber integer FOREIGN KEY REFERENCES Table2(Legnumber));
insert into TABLE1
select '1404', '4/3/2016 5:21:00 AM', NULL union all
select '1404', '4/3/2016 5:34:00 AM', NULL union all
select '1404', '4/3/2016 6:28:00 AM', NULL union all
select '1404', '4/3/2016 2:36:00 PM', NULL union all
select '1404', '4/3/2016 3:13:00 PM', NULL union all
select '1404', '4/3/2016 3:29:00 PM', NULL union all
select '1404', '4/5/2016 8:14:00 AM', NULL union all
select '1404', '4/5/2016 8:26:00 AM', NULL union all
select '1404', '4/5/2016 9:26:00 AM', NULL union all
select '1404', '4/5/2016 3:15:00 PM', NULL union all
select '1404', '4/5/2016 3:53:00 PM', NULL union all
select '1404', '4/5/2016 4:09:00 PM', NULL
;
insert into TABLE2
select '5000', '4/4/2016 3:54:00 AM', 5555555 union all --testing purpose
select '1404', '4/3/2016 5:21:00 AM', 8888888 union all --testing purpose
select '1404', '4/4/2016 3:54:00 AM', 1862405 union all
select '1404', '4/5/2016 4:02:00 AM', 1862410 union all
select '1404', '4/7/2016 6:53:00 AM', 1865901
;
This is the code I used to display, update, then display again for testing.
--This will just display what leg number goes with what
--I will probably be yelled at for not using joins. Normally I do but it worked perfectly fine in this situation to not so I did not bother to.
;WITH LegRows AS (SELECT *, ROW_NUMBER() OVER (ORDER BY t2a.LegStartDate ASC) as rn FROM Table2 t2a)
SELECT t1a.Tractor, t1a.TollExitDateTime, t1a.LegNumber, r1.LegStartDate, r1.LegNumber FROM Table1 t1a, LegRows r1, LegRows r2 WHERE t1a.TollExitDateTime BETWEEN r1.LegStartDate AND r2.LegStartDate AND r1.rn+1 = r2.rn
--This updates the leg information
;WITH LegRows AS (SELECT *, ROW_NUMBER() OVER (ORDER BY t2a.LegStartDate ASC) as rn FROM Table2 t2a)
UPDATE Table1 Set LegNumber = r1.LegNumber FROM Table1 t1a, LegRows r1, LegRows r2 WHERE t1a.TollExitDateTime BETWEEN r1.LegStartDate AND r2.LegStartDate AND r1.rn+1 = r2.rn
--This again displays what leg number goes with what to confirm the update
;WITH LegRows AS (SELECT *, ROW_NUMBER() OVER (ORDER BY t2a.LegStartDate ASC) as rn FROM Table2 t2a)
SELECT t1a.Tractor, t1a.TollExitDateTime, t1a.LegNumber, r1.LegStartDate, r1.LegNumber FROM Table1 t1a, LegRows r1, LegRows r2 WHERE t1a.TollExitDateTime BETWEEN r1.LegStartDate AND r2.LegStartDate AND r1.rn+1 = r2.rn