Group by issue using sql - sql

I am trying to perform aggregation on a table. But it is not aggregating properly for some cases. Please find the below input.
Table t1.
CHANNEL;VALUE;STATUS;ERROR_CODE;RND_TIMESTAMP;SESSION_CD;NAR;
-------------------------------------------------------------
USD;4;12;;2-NOV-2015 11:00:00;;
USD;4;12;;2-NOV-2015 11:00:00;;
USD;2;12;;2-NOV-2015 11:00:00;;
USD;3;12;;2-NOV-2015 11:00:00;;
Output table t2
CHANNEL;VALUE;STATUS;ERROR_CODE;HOUR_TIMESTAMP;SESSION_CD;NAR;
--------------------------------------------------------------
USD;5;12;;2-NOV-2015 11:00:00;;
Query:
select
channel, sum(value),
status, error_code, rnd_timestamp, session_cd, nar
from
t1
where
rnd_timestamp > (select max(hour_timestamp) from t2)
group by
channel, status, error_code, rnd_timestamp, session_cd, nar
Why is it not considering the other 2 rows for aggregation. Is it because some columns in group by have null? How to solve this issue?
Output must be :
USD;13;12;;2-NOV-2015 11:00:00;;

Why do you think your query has an issue?
By switching the hour_timestamp in t2 to be 10am not 11am, your query works as expected for me:
with t1 as (select 'USD' channel, 4 value, 12 status, null error_code, to_date('02/11/2015 11:00:00', 'dd/mm/yyyy hh24:mi:ss') rnd_timestamp, null session_cd, null nar from dual union all
select 'USD' channel, 4 value, 12 status, null error_code, to_date('02/11/2015 11:00:00', 'dd/mm/yyyy hh24:mi:ss') rnd_timestamp, null session_cd, null nar from dual union all
select 'USD' channel, 2 value, 12 status, null error_code, to_date('02/11/2015 11:00:00', 'dd/mm/yyyy hh24:mi:ss') rnd_timestamp, null session_cd, null nar from dual union all
select 'USD' channel, 3 value, 12 status, null error_code, to_date('02/11/2015 11:00:00', 'dd/mm/yyyy hh24:mi:ss') rnd_timestamp, null session_cd, null nar from dual),
t2 as (select 'USD' channel, 5 value, 12 status, null error_code, to_date('02/11/2015 10:00:00', 'dd/mm/yyyy hh24:mi:ss') hour_timestamp, null session_cd, null nar from dual)
--- end of mimicking your tables t1 and t2 with data in; see SQL below:
select channel,
sum(value),
status,
error_code,
rnd_timestamp,
session_cd,
nar
from t1
where rnd_timestamp > (select max(hour_timestamp) from t2)
group by channel,
status,
error_code,
rnd_timestamp,
session_cd,
nar;
CHANNEL SUM(VALUE) STATUS ERROR_CODE RND_TIMESTAMP SESSION_CD NAR
------- ---------- ---------- ---------- --------------------- ---------- ---
USD 13 12 02/11/2015 11:00:00

Related

Oracle: getting an average by week for the timespan of available data

I have some data that shows daily logins by clients on every available date they logged in that streches back a few years.
date month clientId loginCount
------------ --------- ---------- ------------
01/01/2021 01-2021 1234 234
02/01/2021 01-2021 1234 978
01/02/2021 02-2021 6547 45
01/02/2021 02-2021 345 86
....
For each client, I would like to generate the average number of times they login every week for however long they have corresponding date entries in the table :
clientId avgWeeklyLoginCount
---------- ---------------------
1234 125
6547 26
345 48
I understand 'IW' could be used in the TO_CHAR function to do this, e.g.
SELECT
TO_CHAR(date,'IW'),
clientId,
SUM(loginCount) as summedCount
FROM
logins
GROUP BY
TO_CHAR(date,'IW')
but not sure how to get an average by client id from this. any help will be appreciated!
You can using it as example. It can be looks like unnecessary overcomplicated:
ceil((in_date - trunc(to_date('06.01.0001', 'dd.MM.yyyy'), 'IW'))/7)
It means number of week since 1 CE. If your dates contain within single year you can use TO_CHAR(date,'IW') or TO_CHAR(date,'WW') instead of.
with logins(in_date, clientId, loginCount) as (
select to_date('01/01/2021 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 1234, 234 from dual union all
select to_date('02/01/2021 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 1234, 978 from dual union all
select to_date('01/02/2021 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 6547, 45 from dual union all
select to_date('01/02/2021 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 345, 86 from dual union all
select to_date('31/12/2020 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 347, 1 from dual union all
select to_date('01/01/2021 01:00:00', 'dd/MM/yyyy HH:MI:SS'), 347, 1 from dual
)
select
clientId, avg(loginCount) avgLoginCountPerWeek
from (
select
week_number, clientId, sum(loginCount) loginCountPerWeek
from (
select
ceil((in_date - trunc(to_date('06.01.0001', 'dd.MM.yyyy'), 'IW'))/7) week_number, clientId, loginCount
from
logins
) t
group by
week_number, clientId
)
group by
clientId
You can use an aggregation query and count(distinct):
select clientid,
count(*) / count(distinct trunc(in_date, 'WW')) as avg_per_week
from logins
group by clientid;

Query to compare dates patients are missing from hospital census

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

Oracle SQL to find sum of difference of date by Group

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

SQL query to find number of successes between failures

I have an oracle database table that contains test result records. Each record contains the test START_TIME, the INSTRUMENT that the test was performed on, and an ERROR_CODE if an error occurred during the test, among other information.
For every record with an ERROR_CODE equal to '5900', '6900' or '5905', I need to determine the number of successful tests (ERROR_CODE = null) that have occurred on that INSTRUMENT before the datetime of the error record. In other words, I need to know the number of successful tests performed on the instrument before an error was generated.
The database contains over 500 instruments that can each have between 1 and 500,000 test records.
Notes: Only interested in number of successes before ERROR_CODES '5900', '6000' and '5905'. Some instruments may have zero of those errors. Some instruments may have multiple consecutive errors, with no success between them. An error may have occurred on that instrument's first or last test.
Example:
START_TIME INSTRUMENT ERROR_CODE
12/1/2015 22:15:03 A540 null
12/1/2015 22:17:14 A700 null
12/1/2015 22:17:53 A700 null
12/1/2015 22:19:24 A700 5905
12/1/2015 23:28:15 A700 null
12/1/2015 23:35:10 A540 6000
12/2/2015 02:15:13 A540 5900
12/2/2015 03:07:03 A540 null
12/2/2015 03:44:52 A540 null
12/2/2015 09:15:56 A700 null
12/2/2015 14:17:09 A700 5900
12/2/2015 17:15:42 A980 null
12/3/2015 08:17:53 A540 5900
12/3/2015 08:18:49 A540 5900
12/3/2015 11:17:57 A540 null
should give the following results
ERROR_TIME INSTRUMENT SUCCESSES_BEFORE_ERROR
12/1/2015 22:19:24 A700 2
12/1/2015 23:35:10 A540 1
12/2/2015 02:15:13 A540 1
12/2/2015 14:17:09 A700 4
12/3/2015 08:17:53 A540 3
12/3/2015 08:18:49 A540 3
Here's a way using analytic functions:
WITH test_results AS (SELECT to_date('12/01/2015 22:15:03', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/01/2015 22:17:14', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/01/2015 22:17:53', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/01/2015 22:19:24', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, 5905 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/01/2015 23:28:15', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/01/2015 23:35:10', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, 6000 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 02:15:13', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, 5900 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 03:07:03', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 03:44:52', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 09:15:56', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 14:17:09', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A700' instrument, 5900 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/02/2015 17:15:42', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A980' instrument, NULL ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/03/2015 08:17:53', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, 5900 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/03/2015 08:18:49', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, 5900 ERROR_CODE FROM dual UNION ALL
SELECT to_date('12/03/2015 11:17:57', 'mm/dd/yyyy hh24:mi:ss') start_time, 'A540' instrument, NULL ERROR_CODE FROM dual)
-- end of mimicking a table with data in it called "test_results"
-- for use in the following select statement:
SELECT start_time,
instrument,
running_total success_before_error
FROM (SELECT start_time,
instrument,
ERROR_CODE,
sum(CASE WHEN ERROR_CODE IS NOT NULL THEN 0
ELSE 1
END) OVER (PARTITION BY instrument ORDER BY start_time) running_total
FROM test_results)
WHERE ERROR_CODE IS NOT NULL -- this may need to be "error_code in (5900, 6000, 5905)"
ORDER BY start_time;
START_TIME INSTRUMENT SUCCESS_BEFORE_ERROR
------------------- ---------- --------------------
12/01/2015 22:19:24 A700 2
12/01/2015 23:35:10 A540 1
12/02/2015 02:15:13 A540 1
12/02/2015 14:17:09 A700 4
12/03/2015 08:17:53 A540 3
12/03/2015 08:18:49 A540 3
I dont know source table name I call it table_one.
EDIT: As I see now I make a mistake I calculate cosequece of successfull test. I leave it as is
ordered_tab as (
select START_TIME
,INSTRUMENT
,ERROR_CODE
,row_number() over (partition by INSTRUMENT order by START_TIME) rn
from table_one)
select START_TIME as ERROR_TIME
,INSTRUMENT
,SUCCESSES_BEFORE_ERROR
FROM (
select START_TIME
,INSTRUMENT
,ERROR_CODE
,rn -1
- nvl(last_value(nvl2(ERROR_CODE,rn,null) ignore nulls)
over (partition by INSTRUMENT order by START_TIME rows between unbounded preceding and 1 preceding),0) as SUCCESSES_BEFORE_ERROR
from ordered_tab
) where ERROR_CODE IN (5905, 5900, 6000)
There may be a way to do this with analytic functions (no doubt there is). But the simplest way to express the logic -- in my opinion -- is to use a correlated subquery:
select t.*,
(select count(*)
from t t2
where t2.instrument = t.instrument and
t2.start_time < t.start_time and
t2.error_code is null
) as SUCCESSES_BEFORE_ERROR
from t
where t.error_code is not null;

SQL (Oracle) to query for record with max date, only if the end_dt has a value

I am trying to select a record from a row by looking at both the start date and the end date. What I need to do is pick the max start date, then only return a result from that max date if the end date has a value.
I hope the images below help clarify this a bit more. This is in Oracle based SQL.
Example #2
I can, so far, either return all the records or incorrectly return a record in scenario #2 but I've yet to figure out the best way to make this work. I would greatly appreciate any assistance.
Thank you!
I would use an analytic function:
with sample_data as (select 1 id, 1 grp_id, to_date('01/01/2015', 'dd/mm/yyyy') st_dt, to_date('23/01/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 2 id, 1 grp_id, to_date('24/02/2015', 'dd/mm/yyyy') st_dt, to_date('15/02/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 3 id, 1 grp_id, to_date('17/03/2015', 'dd/mm/yyyy') st_dt, to_date('30/03/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 4 id, 2 grp_id, to_date('01/01/2015', 'dd/mm/yyyy') st_dt, to_date('17/01/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 5 id, 2 grp_id, to_date('21/01/2015', 'dd/mm/yyyy') st_dt, to_date('23/03/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 6 id, 2 grp_id, to_date('14/04/2015', 'dd/mm/yyyy') st_dt, to_date('16/05/2015', 'dd/mm/yyyy') ed_dt from dual union all
select 7 id, 2 grp_id, to_date('28/05/2015', 'dd/mm/yyyy') st_dt, null ed_dt from dual),
res as (select id,
grp_id,
st_dt,
ed_dt,
max(st_dt) over (partition by grp_id) max_st_dt
from sample_data)
select id,
grp_id,
st_dt,
ed_dt
from res
where st_dt = max_st_dt
and ed_dt is not null;
ID GRP_ID ST_DT ED_DT
---------- ---------- ---------- ----------
3 1 17/03/2015 30/03/2015
This would be one of the simplest way.
select * from
(
select apay_id,
max(start_dt) OVER () max_start_dt,
start_dt,
end_dt
from sample
)
where
start_dt=max_start_dt
and end_dt is not null
Idea is to get maximum start_dt and corresponding end_dt.
And then filter result if end_dt is null.
SQL Fiddle
Database Schema
create table sample
(apay_id number(7),
account_number number(7),
start_dt date,
end_dt date);
Sample1
insert into sample values(554433, 123456, '15-Aug-15', null);
insert into sample values(112266, 123456, '21-Jul-15', '31-Aug-15');
insert into sample values(733221, 123456, '29-Jun-15', '31-Jul-15');
Output for Sample1
No rows
Sample2
insert into sample values(554433, 123456, '15-Aug-15', '11-Nov-15');
insert into sample values(112266, 123456, '21-Jul-15', '31-Aug-15');
insert into sample values(733221, 123456, '29-Jun-15', '31-Jul-15');
Output for Sample2
| APAY_ID | MAX_START_DT | END_DT |
|---------|--------------------------|----------------------------|
| 554433 | August, 15 2015 00:00:00 | November, 11 2015 00:00:00 |
select * from ( select apay_id from sample where end_dt is not null order by start_dt desc) where rownum=1
I think this can also work.