How would I modify the following SQL query code to return the latest entry of each employee per hour, instead of currently returning every occurrence from each employee regardless of how many times it appears in the query per each hour.
I've attached example data for the order of what currently would be in the database and the desired table output. Note: you can see it's in descending order and 2 entries (0001 and 0009) are omitted because they came from the same employees within an hour interval.
SQL Query Code:
select
TRANSACTION_ID
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
TIME_STAMP
from (select d.*,
row_number() over (partition by EMPLOYEE_ID, trunc(TIME_STAMP, 'HH') order by TIME_STAMP desc) as SEQNUM
from MAIN.DATABASE d
) d
where SEQNUM = 1;
order by TRANSACTION_ID desc;
This is the current order of the data in the database:
TRANSACTION_ID
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
TIME_STAMP
0001
AAAA
Adam
Akbar
10/05/2021 04:42:42.000 PM
0004
BBBB
Barry
Brink
10/06/2021 07:25:25.000 AM
0003
CCCC
Charlie
Che
10/06/2021 07:15:15.000 AM
0005
DDDD
David
Doe
10/06/2021 07:27:27.000 AM
0006
EEEE
Eric
Erickson
10/06/2021 07:29:29.000 AM
0007
FFFF
Fred
Foe
10/06/2021 07:31:31.000 AM
0008
GGGG
George
Guy
10/06/2021 07:33:33.000 AM
0010
HHHH
Henry
Hugh
10/06/2021 07:55:55.000 AM
0009
HHHH
Henry
Hugh
10/06/2021 07:54:54.000 AM
0002
AAAA
Adam
Akbar
10/05/2021 04:43:43.000 PM
This is what should be returned:
TRANSACTION_ID
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
TIME_STAMP
0010
HHHH
Henry
Hugh
10/06/2021 07:55:55.000 AM
0008
GGGG
George
Guy
10/06/2021 07:33:33.000 AM
0007
FFFF
Fred
Foe
10/06/2021 07:31:31.000 AM
0006
EEEE
Eric
Erickson
10/06/2021 07:29:29.000 AM
0005
DDDD
David
Doe
10/06/2021 07:27:27.000 AM
0004
BBBB
Barry
Brink
10/06/2021 07:25:25.000 AM
0003
CCCC
Charlie
Che
10/06/2021 07:15:15.000 AM
0002
AAAA
Adam
Akbar
10/05/2021 04:43:43.000 PM
However, this is what the code is currently returning as written:
TRANSACTION_ID
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
TIME_STAMP
0010
HHHH
Henry
Hugh
10/06/2021 07:55:55.000 AM
0006
EEEE
Eric
Erickson
10/06/2021 07:29:29.000 AM
0003
CCCC
Charlie
Che
10/06/2021 07:15:15.000 AM
0002
AAAA
Adam
Akbar
10/05/2021 04:43:43.000 PM
Any idea what I'm missing and how I can fix it?
The query ( in order to return what you want ) should work as this. However., I am assuming your field time_stamp is really a timestamp in the format you provided.
SQL> set lines 220
SQL> with x ( transaction_id , employee_id, first_name, last_name, time_stamp )
2 as
3 (
4 select '0001' , 'AAAA', 'Adam' , 'Akbar' , to_timestamp('10/05/2021 04:42:42.000 PM','MM/DD/YYYY HH:MI:SS.FF3 PM') from dual union all
5 select '0004' , 'BBBB', 'Barry' , 'Brink' , to_timestamp('10/06/2021 07:25:25.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
6 select '0003' , 'CCCC', 'Charlie' , 'Che' , to_timestamp('10/06/2021 07:15:15.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
7 select '0005' , 'DDDD', 'David' , 'Doe' , to_timestamp('10/06/2021 07:27:27.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
8 select '0006' , 'EEEE', 'Eric' , 'Erickson' , to_timestamp('10/06/2021 07:29:29.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
9 select '0007' , 'FFFF', 'Fred' , 'Foe' , to_timestamp('10/06/2021 07:31:31.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
10 select '0008' , 'GGGG', 'George' , 'Guy' , to_timestamp('10/06/2021 07:33:33.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
11 select '0010' , 'HHHH', 'Henry' , 'Hugh' , to_timestamp('10/06/2021 07:55:55.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
12 select '0009' , 'HHHH', 'Henry' , 'Hugh' , to_timestamp('10/06/2021 07:54:54.000 AM','MM/DD/YYYY HH:MI:SS.FF3 AM') from dual union all
13 select '0002' , 'AAAA', 'Adam' , 'Akbar' , to_timestamp('10/05/2021 04:43:43.000 PM','MM/DD/YYYY HH:MI:SS.FF3 PM') from dual
14 )
15 select
16 TRANSACTION_ID ,
17 EMPLOYEE_ID ,
18 FIRST_NAME ,
19 LAST_NAME ,
20 TIME_STAMP
21 from (select x.*,
22 row_number() over (partition by EMPLOYEE_ID, trunc(TIME_STAMP, 'HH') order by TIME_STAMP desc) as SEQNUM
23 from x
24 )
25 where SEQNUM = 1
26* order by TRANSACTION_ID desc
TRAN EMPL FIRST_N LAST_NAM TIME_STAMP
---- ---- ------- -------- ---------------------------------------------------------------------------
0010 HHHH Henry Hugh 06-OCT-21 07.55.55.000000000 AM
0008 GGGG George Guy 06-OCT-21 07.33.33.000000000 AM
0007 FFFF Fred Foe 06-OCT-21 07.31.31.000000000 AM
0006 EEEE Eric Erickson 06-OCT-21 07.29.29.000000000 AM
0005 DDDD David Doe 06-OCT-21 07.27.27.000000000 AM
0004 BBBB Barry Brink 06-OCT-21 07.25.25.000000000 AM
0003 CCCC Charlie Che 06-OCT-21 07.15.15.000000000 AM
0002 AAAA Adam Akbar 05-OCT-21 04.43.43.000000000 PM
8 rows selected.
SQL>
Your query is:
missing commas between the terms in the SELECT clause; and
has a ; after the WHERE filter and before the ORDER BY clause.
If you fix those issues then you get the code:
select TRANSACTION_ID,
EMPLOYEE_ID,
FIRST_NAME,
LAST_NAME,
TIME_STAMP
from (
select d.*,
row_number() over (
partition by EMPLOYEE_ID, trunc(TIME_STAMP, 'HH')
order by TIME_STAMP desc
) as SEQNUM
from MAIN.DATABASE d
) d
where SEQNUM = 1
order by TRANSACTION_ID desc;
Which, for the sample data:
CREATE TABLE main.database (TRANSACTION_ID, EMPLOYEE_ID, FIRST_NAME, LAST_NAME, TIME_STAMP ) AS
SELECT '0001', 'AAAA', 'Adam', 'Akbar', TIMESTAMP '2021-05-10 16:42:42.000' FROM DUAL UNION ALL
SELECT '0004', 'BBBB', 'Barry', 'Brink', TIMESTAMP '2021-06-10 07:25:25.000' FROM DUAL UNION ALL
SELECT '0003', 'CCCC', 'Charlie', 'Che', TIMESTAMP '2021-06-10 07:15:15.000' FROM DUAL UNION ALL
SELECT '0005', 'DDDD', 'David', 'Doe', TIMESTAMP '2021-06-10 07:27:27.000' FROM DUAL UNION ALL
SELECT '0006', 'EEEE', 'Eric', 'Erickson', TIMESTAMP '2021-06-10 07:29:29.000' FROM DUAL UNION ALL
SELECT '0007', 'FFFF', 'Fred', 'Foe', TIMESTAMP '2021-06-10 07:31:31.000' FROM DUAL UNION ALL
SELECT '0008', 'GGGG', 'George', 'Guy', TIMESTAMP '2021-06-10 07:33:33.000' FROM DUAL UNION ALL
SELECT '0010', 'HHHH', 'Henry', 'Hugh', TIMESTAMP '2021-06-10 07:55:55.000' FROM DUAL UNION ALL
SELECT '0009', 'HHHH', 'Henry', 'Hugh', TIMESTAMP '2021-06-10 07:54:54.000' FROM DUAL UNION ALL
SELECT '0002', 'AAAA', 'Adam', 'Akbar', TIMESTAMP '2021-05-10 16:43:43.000' FROM DUAL;
Works as expected:
TRANSACTION_ID
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
TIME_STAMP
0010
HHHH
Henry
Hugh
10-JUN-21 07.55.55.000000000
0008
GGGG
George
Guy
10-JUN-21 07.33.33.000000000
0007
FFFF
Fred
Foe
10-JUN-21 07.31.31.000000000
0006
EEEE
Eric
Erickson
10-JUN-21 07.29.29.000000000
0005
DDDD
David
Doe
10-JUN-21 07.27.27.000000000
0004
BBBB
Barry
Brink
10-JUN-21 07.25.25.000000000
0003
CCCC
Charlie
Che
10-JUN-21 07.15.15.000000000
0002
AAAA
Adam
Akbar
10-MAY-21 16.43.43.000000000
db<>fiddle here
Related
I am using Oracle SQL, and I'm trying to perform a historical test score analysis (to visualize test score improvements on per month basis for individuals). Firstly, I have a table that is a list of Users and the respective Months they are active; it looks something like this:
TABLE1
________________________
UserName | ActiveDate
________________________
John Doe, 01-MAY-18
John Doe, 01-APR-18
John Doe, 01-MAR-18
Jane Doe, 01-APR-18
Jane Doe, 01-MAR-18
Jim Doe, 01-MAY-18
On top of that, I have another table that lists Test Scores, which are timestamped (you can retake the test as many times as you want). It looks something like this:
TABLE2
________________________________________
UserName | TestScore | EffectiveDate
________________________________________
John Doe, 87, 07-FEB-18
John Doe, 85, 14-FEB-18
John Doe, 90, 18-FEB-18
John Doe, 92, 02-MAR-18
John Doe, 91, 12-MAR-18
Jane Doe, 70, 01-FEB-18
Jane Doe, 72, 02-FEB-18
Jane Doe, 78, 18-FEB-18
Jane Doe, 77, 06-MAR-18
Jane Doe, 81, 18-MAR-18
Jim Doe, 50, 03-MAR-18
Jim Doe, 48, 23-MAR-18
Jim Doe, 58, 08-APR-18
For every row in the first table (all the UserName | ActiveDate pairings are disctinct), I would like to select the most recent TestScore from Table2 where the EffectiveDate is prior to the ActiveDate
So I'm hoping to get something like this
UserName | ActiveDate | Most recent TestScore prior to ActiveDate
______________________________________
John Doe, 01-MAY-18, 91
John Doe, 01-APR-18, 91
John Doe, 01-MAR-18, 90
Jane Doe, 01-APR-18, 81
Jane Doe, 01-MAR-18, 78
Jim Doe, 01-MAY-18, 58
I've tried to make this work by JOINING Table1 to Table2 on UserName, where EffectiveDate < ActiveDate, but I can't seem to figure out the SQL statement I need to SELECT * from Table2 where EffectiveDate < ActiveDate, but I'm struggling to figure that out on a "per row" basis...
Thanks for any and all advice in advance. This is my first posting to StackOverflow, so I hope I've posed this question correctly!
Edit: Thanks all for the help, I think I have everything I need to proceed with my project now. I'll be sure to make some improvements to my posting next time I ask a question here on SO.
Two solutions that only require a single join:
Oracle Setup:
CREATE TABLE TABLE1 ( UserName, ActiveDate ) AS
SELECT 'John Doe', DATE '2018-05-01' FROM DUAL UNION ALL
SELECT 'John Doe', DATE '2018-04-01' FROM DUAL UNION ALL
SELECT 'John Doe', DATE '2018-03-01' FROM DUAL UNION ALL
SELECT 'Jane Doe', DATE '2018-04-01' FROM DUAL UNION ALL
SELECT 'Jane Doe', DATE '2018-03-01' FROM DUAL UNION ALL
SELECT 'Jim Doe', DATE '2018-05-01' FROM DUAL;
CREATE TABLE TABLE2 ( UserName, TestScore, EffectiveDate ) AS
SELECT 'John Doe', 87, DATE '2018-02-07' FROM DUAL UNION ALL
SELECT 'John Doe', 85, DATE '2018-02-14' FROM DUAL UNION ALL
SELECT 'John Doe', 90, DATE '2018-02-18' FROM DUAL UNION ALL
SELECT 'John Doe', 92, DATE '2018-03-02' FROM DUAL UNION ALL
SELECT 'John Doe', 91, DATE '2018-03-12' FROM DUAL UNION ALL
SELECT 'Jane Doe', 70, DATE '2018-02-01' FROM DUAL UNION ALL
SELECT 'Jane Doe', 72, DATE '2018-02-02' FROM DUAL UNION ALL
SELECT 'Jane Doe', 78, DATE '2018-02-18' FROM DUAL UNION ALL
SELECT 'Jane Doe', 77, DATE '2018-03-06' FROM DUAL UNION ALL
SELECT 'Jane Doe', 81, DATE '2018-03-18' FROM DUAL UNION ALL
SELECT 'Jim Doe', 50, DATE '2018-03-03' FROM DUAL UNION ALL
SELECT 'Jim Doe', 48, DATE '2018-03-23' FROM DUAL UNION ALL
SELECT 'Jim Doe', 58, DATE '2018-04-08' FROM DUAL;
Query 1:
SELECT *
FROM (
SELECT t2.*,
t1.ActiveDate,
ROW_NUMBER() OVER ( PARTITION BY t2.UserName, t1.ActiveDate ORDER BY EffectiveDate DESC ) AS rn
FROM table2 t2
INNER JOIN
table1 t1
ON ( t1.UserName = t2.UserName
AND t2.EffectiveDate < t1.ActiveDate )
) t2
WHERE rn = 1;
Output:
USERNAME TESTSCORE EFFECTIVEDATE ACTIVEDATE RN
---------- ---------- -------------- ---------- ---
Jane Doe 78 18-FEB-18 01-MAR-18 1
Jane Doe 81 18-MAR-18 01-APR-18 1
Jim Doe 58 08-APR-18 01-MAY-18 1
John Doe 90 18-FEB-18 01-MAR-18 1
John Doe 91 12-MAR-18 01-APR-18 1
John Doe 91 12-MAR-18 01-MAY-18 1
Query 2:
SELECT t1.UserName,
t1.ActiveDate,
MAX( TestScore ) KEEP ( DENSE_RANK LAST ORDER BY EffectiveDate ) AS MostRecentTestScore
FROM table2 t2
INNER JOIN
table1 t1
ON ( t1.UserName = t2.UserName
AND t2.EffectiveDate < t1.ActiveDate )
GROUP BY t1.UserName, t1.ActiveDate;
Output:
USERNAME ACTIVEDATE MOSTRECENTTESTSCORE
---------- ---------- -------------------
Jim Doe 01-MAY-18 58
Jane Doe 01-MAR-18 78
Jane Doe 01-APR-18 81
John Doe 01-MAR-18 90
John Doe 01-APR-18 91
John Doe 01-MAY-18 91
If you just want the test score, a correlated subquery might be the simplest approach:
select t1.*,
(select max(t2.score) keep (dense_rank first order by t2.effectivedate desc)
from table2 t2
where t2.effectivedate < t1.activedate
) as most_recent_score
from table1 t1;
Here's one option (you need lines 24 onwards; previous lines are just testing CTEs):
SQL> with table1 (username, activedate) as
2 (select 'jod', date '2018-05-01' from dual union all
3 select 'jod', date '2018-04-01' from dual union all
4 select 'jod', date '2018-03-01' from dual union all
5 select 'jad', date '2018-04-01' from dual union all
6 select 'jad', date '2018-03-01' from dual union all
7 select 'jid', date '2018-05-01' from dual
8 ),
9 table2 (username, testscore, effectivedate) as
10 (select 'jod', 87, date '2018-02-07' from dual union all
11 select 'jod', 85, date '2018-02-14' from dual union all
12 select 'jod', 90, date '2018-02-18' from dual union all
13 select 'jod', 92, date '2018-03-02' from dual union all
14 select 'jod', 91, date '2018-03-12' from dual union all
15 select 'jad', 70, date '2018-02-01' from dual union all
16 select 'jad', 72, date '2018-02-02' from dual union all
17 select 'jad', 78, date '2018-02-18' from dual union all
18 select 'jad', 77, date '2018-03-06' from dual union all
19 select 'jad', 81, date '2018-03-18' from dual union all
20 select 'jid', 50, date '2018-03-03' from dual union all
21 select 'jid', 48, date '2018-03-23' from dual union all
22 select 'jid', 58, date '2018-04-08' from dual
23 )
24 select t1.username, t1.activedate, t2.testscore
25 from table1 t1 join table2 t2 on t1.username = t2.username
26 where t2.effectivedate = (select max(t2a.effectivedate)
27 from table2 t2a
28 where t2a.username = t2.username
29 and t2a.effectivedate < t1.activedate
30 )
31 order by t1.username, t1.activedate desc;
USE ACTIVEDAT TESTSCORE
--- --------- ----------
jad 01-apr-18 81
jad 01-mar-18 78
jid 01-may-18 58
jod 01-may-18 91
jod 01-apr-18 91
jod 01-mar-18 90
6 rows selected.
SQL>
I have data in my oracle table where I have names and date rages as following:
Name From To
Lopes, Janine 07-Jun-17 16-Jul-17
Lopes, Janine 17-Jul-17 23-Jul-17
Lopes, Janine 24-Jul-17 31-Aug-17
Baptista, Maria 23-Dec-16 19-Feb-17
Deyak,Sr, Thomas 22-Jan-17 18-Apr-17
Deyak,Sr, Thomas 27-Apr-17 14-May-17
Deyak,Sr, Thomas 15-May-17 21-May-17
Deyak,Sr, Thomas 22-May-17 28-May-17
Deyak,Sr, Thomas 29-May-17 31-May-17
Serrentino, Joyce 18-Mar-17 30-Apr-17
More, Cathleen 30-Jul-17 13-Aug-17
More, Cathleen 14-Aug-17 20-Aug-17
More, Cathleen 21-Aug-17 27-Aug-17
More, Cathleen 28-Aug-17 03-Sep-17
More, Cathleen 04-Sep-17 10-Sep-17
More, Cathleen 11-Sep-17 24-Sep-17
Barrows, Michael 30-Jan-17 19-Mar-17
Barrows, Michael 20-Mar-17 26-Mar-17
Barrows, Michael 27-Mar-17 02-Apr-17
Barrows, Michael 03-Apr-17 07-Apr-17
Mostly for one user the to date is one greater than from date and is continuous but in some cases there is break the data so my output should look like this:
Name From To
Lopes, Janine 07-Jun-17 31-Aug-17
Baptista, Maria 23-Dec-16 19-Feb-17
Deyak,Sr, Thomas 22-Jan-17 18-Apr-17
Deyak,Sr, Thomas 27-Apr-17 31-May-17
Serrentino, Joyce 18-Mar-17 30-Apr-17
More, Cathleen 30-Jul-17 24-Sep-17
Barrows, Michael 30-Jan-17 07-Apr-17
If I do min(from) and max(to) I loose some records like for Thomas.
How should I write sql to get the data is I require.
In Oracle 12.1 and above, the MATCH_RECOGNIZE clause does quick work of such requirements. I am using the same setup and simulated data (WITH clause) from my other answer, and the output is also the same.
select name, date_fr, date_to
from inputs
match_recognize(
partition by name
order by date_fr
measures a.date_fr as date_fr,
last(date_to) as date_to
pattern ( a b* )
define b as date_fr = prev(date_to) + 1
)
;
This can be solved nicely with the Tabibitosan method.
Preparation:
alter session set nls_date_format = 'dd-Mon-rr';
Session altered.
;
Query (including simulated inputs for convenience):
with
inputs ( name, date_fr, date_to ) as (
select 'Lopes, Janine' , to_date('07-Jun-17'), to_date('16-Jul-17') from dual union all
select 'Lopes, Janine' , to_date('17-Jul-17'), to_date('23-Jul-17') from dual union all
select 'Lopes, Janine' , to_date('24-Jul-17'), to_date('31-Aug-17') from dual union all
select 'Baptista, Maria' , to_date('23-Dec-16'), to_date('19-Feb-17') from dual union all
select 'Deyak,Sr, Thomas' , to_date('22-Jan-17'), to_date('18-Apr-17') from dual union all
select 'Deyak,Sr, Thomas' , to_date('27-Apr-17'), to_date('14-May-17') from dual union all
select 'Deyak,Sr, Thomas' , to_date('15-May-17'), to_date('21-May-17') from dual union all
select 'Deyak,Sr, Thomas' , to_date('22-May-17'), to_date('28-May-17') from dual union all
select 'Deyak,Sr, Thomas' , to_date('29-May-17'), to_date('31-May-17') from dual union all
select 'Serrentino, Joyce', to_date('18-Mar-17'), to_date('30-Apr-17') from dual union all
select 'More, Cathleen' , to_date('30-Jul-17'), to_date('13-Aug-17') from dual union all
select 'More, Cathleen' , to_date('14-Aug-17'), to_date('20-Aug-17') from dual union all
select 'More, Cathleen' , to_date('21-Aug-17'), to_date('27-Aug-17') from dual union all
select 'More, Cathleen' , to_date('28-Aug-17'), to_date('03-Sep-17') from dual union all
select 'More, Cathleen' , to_date('04-Sep-17'), to_date('10-Sep-17') from dual union all
select 'More, Cathleen' , to_date('11-Sep-17'), to_date('24-Sep-17') from dual union all
select 'Barrows, Michael' , to_date('30-Jan-17'), to_date('19-Mar-17') from dual union all
select 'Barrows, Michael' , to_date('20-Mar-17'), to_date('26-Mar-17') from dual union all
select 'Barrows, Michael' , to_date('27-Mar-17'), to_date('02-Apr-17') from dual union all
select 'Barrows, Michael' , to_date('03-Apr-17'), to_date('07-Apr-17') from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select name, min(date_fr) as date_fr, max(date_to) as date_to
from ( select name, date_fr, date_to,
date_to - sum( date_to - date_fr + 1 ) over (partition by name
order by date_fr) as gr
from inputs
)
group by name, gr
order by name, date_fr
;
Output:
NAME DATE_FR DATE_TO
----------------- --------- ---------
Baptista, Maria 23-Dec-16 19-Feb-17
Barrows, Michael 30-Jan-17 07-Apr-17
Deyak,Sr, Thomas 22-Jan-17 18-Apr-17
Deyak,Sr, Thomas 27-Apr-17 31-May-17
Lopes, Janine 07-Jun-17 31-Aug-17
More, Cathleen 30-Jul-17 24-Sep-17
Serrentino, Joyce 18-Mar-17 30-Apr-17
7 rows selected
I created a table from a huge database after running multiple queries like the following is the sample:
communication_day | comid | person_id | area_id | w_id
20-MAR-17 03:45 | 21 | xyz1 | y123 | S1
20-MAR-17 07:45 | 21 | xyz1 | y142 | S2
14-MAR-17 07:45 | 41 | xyz1 | y153 | S1
14-MAR-17 09:00 | 41 | xyz1 | y123 | S3
30-MAR-17 09:00 | 88 | abc1 | y180 | D1
30-MAR-17 11:00 | 88 | abc1 | y181 | D2
Basically, a person (represented by person_id) comes in and requests change in its area id, this could be done be a person multiple times. Each time a person requests this, he is given a comid. So, like in the above example person_id = xyz1 first requests a change from y153 to y123 and then on 20th march, requests a change from y123 to y142.
I want to create a new column in this table called old_w_id such that for each change request (each comid) it has that old value in it. Eg for the above sample:
old_w_id
S1
S1
S1
S1
D1
D1
Cases where request came in for that comid, the old_w_id can have the same value only. Only where the change request has taken effect, old_w_id should have the previous value.
Any idea how do I do that?? Thanks in advance!!
UPDATE: Each comid might not necessarily have 2 rows i.e, a person might have requested the change but it might not have been acknowledged yet (that gives just one row for that comid at this time).
Yes of course I can explain. partition by clause tells analytic function to do calculation partitioned by column you specified, in our case it's the person_id, otherwise lag function will work for all rows through your returned row set. And also I'll remove default value for first record, I'll change lag(w_id, 1, w_id) to lag(w_id, 1) it's more correct to show null, because there is no lag w_id for it. Something like this.
with my_table as
(
select to_date('20-MAR-17 03:45', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 21 as comid,
'xyz1' as person_id, 'y123' as area_id, 'S1' w_id
from dual
union all
select to_date('20-MAR-17 03:45', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 21 as comid,
'xyz1' as person_id, 'y142' as area_id, 'S2' w_id
from dual
union all
select to_date('14-MAR-17 07:45', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 41 as comid,
'xyz1' as person_id, 'y153' as area_id, 'S1' w_id
from dual
union all
select to_date('14-MAR-17 09:00', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 41 as comid,
'xyz1' as person_id, 'y123' as area_id, 'S3' w_id
from dual
union all
select to_date('30-MAR-17 09:05', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 88 as comid,
'abc1' as person_id, 'y180' as area_id, 'D1' w_id
from dual
union all
select to_date('30-MAR-17 11:00', 'DD-MON-YY HH24:MI',
'nls_date_language=english') as communication_day, 88 as comid,
'abc1' as person_id, 'y181' as area_id, 'D2' w_id
from dual)
select t.*,
lag(w_id, 1) over(partition by person_id, comid order by communication_day) as old_w_id
from my_table t
out put will be
COMMUNICATION_DAY COMID PERSON_ID AREA_ID W_ID OLD_W_ID
1 30/03/17 09:05:00 88 abc1 y180 D1
2 30/03/17 11:00:00 88 abc1 y181 D2 D1
3 14/03/17 07:45:00 41 xyz1 y153 S1
4 14/03/17 09:00:00 41 xyz1 y123 S3 S1
5 20/03/17 03:45:00 21 xyz1 y123 S1
6 20/03/17 03:45:00 21 xyz1 y142 S2 S1
I have a table which i want to only output 1 field from so it can be part of another queries WHERE statement (WHERE SID IN (THIS NEW QUERY)).
However because of this i can only include SID in the SELECT, but this is removing that is needed to make the distinct count work.
SO SELECT * FROM Tablea gives me:
SID deta detb
22222 8159 3763
22222 8159 3763
44444 4739 6135
44444 4739 6135
44444 4739 6134
44444 4739 6135
55555 5937 0223
55555 5936 0223
66666 8577 9497
66666 8577 9497
66666 8577 9497
66666 8576 9496
66666 8577 9497
88888 3595 0919
88888 3595 0919
88888 3595 0919
88888 3595 0914
77777 5678 3456
Then SELECT DISTINCT SID, deta, detb FROM Tablea gives me:
SID deta detb
22222 8159 3763
44444 4739 6134
44444 4739 6135
55555 5936 0223
55555 5937 0223
66666 8576 9496
66666 8577 9497
88888 3595 0914
88888 3595 0919
77777 5678 3456
The data i want is this:
SID deta detb
44444 4739 6134
44444 4739 6135
55555 5936 0223
55555 5937 0223
66666 8576 9496
66666 8577 9497
88888 3595 0914
88888 3595 0919
Which can be done by using a count of distinct, however my final output i want is this:
SID
44444
55555
66666
88888
But i cant achieve it when only outputting 1 field.
Use group by:
SELECT SID
FROM Tablea
GROUP BY SID
HAVING COUNT(DISTINCT deta || ':' || detab) > 1;
If you actually wanted the full rows (instead of the SID values), then use window functions:
SELECT a.*
FROM (SELECT a.*, COUNT(DISTINCT deta || ':' || detab) OVER (PARTITION BY SID) as cnt
FROM tablea a
) a
WHERE cnt > 1;
I would do it like this:
with sample_data (SID, deta, detb) as (select 22222, 8159, 3763 from dual union all
select 22222, 8159, 3763 from dual union all
select 44444, 4739, 6135 from dual union all
select 44444, 4739, 6135 from dual union all
select 44444, 4739, 6134 from dual union all
select 44444, 4739, 6135 from dual union all
select 55555, 5937, 0223 from dual union all
select 55555, 5936, 0223 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8576, 9496 from dual union all
select 66666, 8577, 9497 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0914 from dual union all
select 77777, 5678, 3456 from dual)
--- end of mimicking your sample data
select sid
from (select distinct sid,
deta,
detb
from sample_data)
group by sid
having count(*) > 1;
SID
----------
44444
66666
55555
88888
I'm new to Oracle and I need to help with this query. I have table with data samples /records like:
name | datetime
-----------
A | 20140414 10:00
A | 20140414 10:30
A | 20140414 11:00
B | 20140414 11:30
B | 20140414 12:00
A | 20140414 12:30
A | 20140414 13:00
A | 20140414 13:30
And I need to "group"/get informations into this form:
name | datetime_from | datetime_to
----------------------------------
A | 20140414 10:00 | 20140414 11:00
B | 20140414 11:30 | 20140414 12:00
A | 20140414 12:30 | 20140414 13:30
I couldnt find any solution for query similar to this. Could anyone please help me?
Note: I dont want do use temporary tables.
Thanks,
Pavel
SQL> with t (name, datetime) as
2 (
3 select 'A', to_date('20140414 10:00','YYYYMMDD HH24:MI') from dual union all
4 select 'A', to_date('20140414 10:30','YYYYMMDD HH24:MI') from dual union all
5 select 'A', to_date('20140414 11:00','YYYYMMDD HH24:MI') from dual union all
6 select 'B', to_date('20140414 11:30','YYYYMMDD HH24:MI') from dual union all
7 select 'B', to_date('20140414 12:00','YYYYMMDD HH24:MI') from dual union all
8 select 'A', to_date('20140414 12:30','YYYYMMDD HH24:MI') from dual union all
9 select 'A', to_date('20140414 13:00','YYYYMMDD HH24:MI') from dual union all
10 select 'A', to_date('20140414 13:30','YYYYMMDD HH24:MI') from dual
11 )
12 select name, min(datetime) datetime_from, max(datetime) datetime_to
13 from (
14 select name, datetime,
15 datetime-(1/48)*(row_number() over(partition by name order by datetime)) dt
16 from t
17 )
18 group by name,dt
19 order by 2,1
20 /
N DATETIME_FROM DATETIME_TO
- -------------- --------------
A 20140414 10:00 20140414 11:00
B 20140414 11:30 20140414 12:00
A 20140414 12:30 20140414 13:30
You need to find periods where the values are the same. The easiest way in Oracle is to use the lag() function, some logic, and aggregation:
select name, min(datetime), max(datetime)
from (select t.*,
sum(case when name <> prevname then 1 else 0 end) over (order by datetime) as cnt
from (select t.*, lag(name) over (order by datetime) as prevname
from table t
) t
) t
group by name, cnt;
What this does is count, for a given value of datetime, the number of times that the name has switched on or before that datetime. This identifies the periods of "constancy", which are then used for aggregation.
As 9000 is suggesting you can have a query like the following:
select
a.name,
Max(a.datetime),
Min(b.datetime)
from
table a,
table b
group by
a.name
where a.name = b.name