How can I select only newest entries? - sql

I have table like this:
IST | FILEDATE | DATE | ...
1 | 2013-2014 | 27.03.2015 10:20:47 | ...
2 | 2013-2014 | 27.03.2015 10:20:47 | ...
3 | 2013-2014 | 27.03.2015 10:20:47 | ...
1 | 2013-2014 | 28.03.2015 11:20:47 | ...
2 | 2013-2014 | 28.03.2015 11:20:47 | ...
3 | 2013-2014 | 28.03.2015 11:20:47 | ...
1 | 2014-2015 | 29.03.2015 12:20:47 | ...
2 | 2014-2015 | 29.03.2015 12:20:47 | ...
3 | 2014-2015 | 29.03.2015 12:20:47 | ...
...
I need to select newest(with date value) entry of all IST, like this:
IST | FILEDATE | DATE | ...
1 | 2014-2015 | 29.03.2015 11:20:47 | ...
2 | 2014-2015 | 29.03.2015 11:20:47 | ...
3 | 2014-2015 | 29.03.2015 11:20:47 | ...
I tried order by and rownum=1, but its working for just single IST.
How can I do that? Thank you.

That's a typical scenario where analytical functions (aka windowing functions) are really helpful:
with v_data(ist, filedate, entry_date) as (
select 1, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 1, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 1, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual)
select * from (
select
v1.*,
row_number() over (partition by ist order by entry_date desc) as rn
from v_data v1
)
where rn=1
This solution
computes an ordering per group using the ROW_NUMBER analytical function
removes everything but the newest entry per group with WHERE rn = 1

You can first group the result:
select ist, max(date) date
from table
group
by ist
Then you can combine that result with a select to get all matching lines:
select master.*
from table master
join
( select ist, max(date) date
from table
group
by ist
) filter
on master.ist = filter.ist
and master.date = filter.date

Use NOT EXISTS to find ist's that have no newer row in table:
select *
from tablename t1
where not exists (select 1 from tablename t2
where t2.ist = t1.ist
and t2.date > t1.date)*

Related

With Oracle SQL how can I find 3 days where total sum >= 150

I have a report that needs to list activity where total is >= 150 over 3 consecutive days.
Let's say I've created a temp table foo, to summarize daily totals.
| ID | Day | Total |
| -- | ---------- | ----- |
| 01 | 2020-01-01 | 10 |
| 01 | 2020-01-02 | 50 |
| 01 | 2020-01-03 | 50 |
| 01 | 2020-01-04 | 50 |
| 01 | 2020-01-05 | 20 |
| 02 | 2020-01-01 | 10 |
| 02 | 2020-01-02 | 10 |
| 02 | 2020-01-03 | 10 |
| 02 | 2020-01-04 | 10 |
| 02 | 2020-01-05 | 10 |
How Would I write SQL to return ID 01, but not 02?
Example Result:
| ID |
| -- |
| 01 |
I suspect that you want window functions here:
select distinct id
from (
select
t.*,
sum(total) over(partition by id order by day rows between 2 preceding and current row) sum_total,
count(*) over(partition by id order by day rows between 2 preceding and current row) cnt
from mytable t
) t
where cnt = 3 and sum_total >= 150
This gives you the ids that have a total greater than the given threshold over 3 consecutive days - which is how I understood your question.
If you just want to output the rows that have 3 consecutive days with a sum >= 150, you can use an analytic function to determine the moving total across each 3 day period per id, and then find the aggregate max value of the moving total per id, returning the id where it's >= 150.
E.g.:
WITH your_table AS (SELECT 1 ID, to_date('01/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual UNION ALL
SELECT 1 ID, to_date('02/01/2020', 'dd/mm/yyyy') dy, 50 total FROM dual UNION ALL
SELECT 1 ID, to_date('03/01/2020', 'dd/mm/yyyy') dy, 50 total FROM dual UNION ALL
SELECT 1 ID, to_date('04/01/2020', 'dd/mm/yyyy') dy, 50 total FROM dual UNION ALL
SELECT 1 ID, to_date('05/01/2020', 'dd/mm/yyyy') dy, 20 total FROM dual UNION ALL
SELECT 2 ID, to_date('01/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual UNION ALL
SELECT 2 ID, to_date('02/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual UNION ALL
SELECT 2 ID, to_date('03/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual UNION ALL
SELECT 2 ID, to_date('04/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual UNION ALL
SELECT 2 ID, to_date('05/01/2020', 'dd/mm/yyyy') dy, 10 total FROM dual),
moving_sums AS (SELECT ID,
dy,
total,
SUM(total) OVER (PARTITION BY ID ORDER BY dy RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) moving_sum
FROM your_table)
SELECT ID
FROM moving_sums
GROUP BY ID
HAVING MAX(moving_sum) >= 150;
ID
----------
1
You can use a HAVING Clause GROUPED BY ID to list the desired ID values
SELECT ID
FROM foo
GROUP BY ID
HAVING COUNT( distinct day )>=3 AND SUM( NVL(Total,0) ) >= 150
Demo
Use this if you are to specify dates
WITH foo( ID, Day, Total ) AS
(
SELECT '01', date'2020-01-01' , 10 FROM dual
UNION ALL SELECT '01', date'2020-01-02' , 50 FROM dual
UNION ALL SELECT '01', date'2020-01-03' , 50 FROM dual
UNION ALL SELECT '01', date'2020-01-04' , 50 FROM dual
UNION ALL SELECT '01', date'2020-01-05' , 20 FROM dual
UNION ALL SELECT '02', date'2020-01-01' , 10 FROM dual
UNION ALL SELECT '02', date'2020-01-02' , 10 FROM dual
UNION ALL SELECT '02', date'2020-01-03' , 10 FROM dual
UNION ALL SELECT '02', date'2020-01-04' , 10 FROM dual
UNION ALL SELECT '02', date'2020-01-05' , 10 FROM dual
)SELECT
ID
FROM foo
WHERE day BETWEEN TO_DATE('2020-01-01', 'YYYY-MM-DD' ) AND TO_DATE('2020-01-04', 'YYYY-MM-DD' )
GROUP BY ID HAVING SUM(Total) >= 150;
RESULT:
ID|
--|
01|
Maybe you can try something like this :
SELECT
*
FROM foo
WHERE day BETWEEN 2020-01-01 AND 2020-01-04
AND total > 150

Can Not Group By on DATE from Timestamp

I am unable to group by on date from a timestamp column in below query:
CHG_TABLE
+----+--------+----------------+-----------------+-------+-----------+
| Key|Seq_Num | Start_Date | End_Date | Value |Record_Type|
+----+--------+----------------+-----------------+-------+-----------+
| 1 | 1 | 5/25/2019 2.05 | 12/31/9999 00.00| 800 | Insert |
| 1 | 1 | 5/25/2019 2.05 | 5/31/2019 11.12 | 800 | Update |
| 1 | 2 | 5/31/2019 11.12| 12/31/9999 00.00| 900 | Insert |
| 1 | 2 | 5/31/2019 11.12| 6/15/2019 12.05 | 900 | Update |
| 1 | 3 | 6/15/2019 12.05| 12/31/9999 00.00| 1000 | Insert |
| 1 | 3 | 6/15/2019 12.05| 6/25/2019 10.20 | 1000 | Update |
+---+---------+----------------+-----------------+-------+-----------+
RESULT:
+-----+------------------+----------------+-----------+----------+
| Key | Month_Start_Date | Month_End_Date |Begin_Value|End_Value |
+---- +------------------+----------------+-----------+----------+
| 1 | 6/1/2019 | 6/30/2019 | 1700 | 1000 |
| 1 | 7/1/2019 | 7/31/2019 | 1000 | 1000 |
+-----+------------------+----------------+-----------+----------+
Begin_Value : Sum(Value) for Max(Start_Date) < Month_Start_Date -> Should pick up latest date from last month
End_Value : Sum(Value) for Max(Start_Date) <= Month_End_Date -> Should pick up the latest date
SELECT k.key,
dd.month_start_date,
dd.month_end_date,
gendata.value first_value,
gendata.next_value last_value
FROM dim_date dd CROSS JOIN dim_person k
JOIN (SELECT ct.key,
dateadd('day',1,last_day(ct.start_date)) start_date ,
SUM(ct.value),
lead(SUM(ct.value)) OVER(ORDER BY ct.start_date) next_value
FROM (SELECT key,to_char(start_Date,'MM-YYYY') MMYYYY, max(start_Date) start_date
FROM CHG_TABLE
GROUP BY to_char(start_Date,'MM-YYYY'), key
) dt JOIN CHG_TABLE ct ON
dt.start_date = ct.start_date AND
dt.key = ct.key
group by ct.key, to_char(start_Date,'MM-YYYY')
) gendata ON
to_char(dd.month_end_date,'MM-YYYY') = to_char(to_char(start_Date,'MM-YYYY')) AND
k.key = gendata.key;
Error:
start_Date is not a valid group by expression
Related post:
Monthly Snapshot using Date Dimension
Hoping, I understood your question correctly.
You can check below query
WITH chg_table ( key, seq_num, start_date, end_date, value, record_type ) AS
(
SELECT 1,1,TO_DATE('5/25/2019 2.05','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 800, 'Insert' FROM DUAL UNION ALL
SELECT 1,1,TO_DATE('5/25/2019 2.05','MM/DD/YYYY HH24.MI'),TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'), 800, 'Update' FROM DUAL UNION ALL
SELECT 1,2,TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 900, 'Insert' FROM DUAL UNION ALL
SELECT 1,2,TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'),TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'), 900, 'Update' FROM DUAL UNION ALL
SELECT 1,3,TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 1000, 'Insert' FROM DUAL UNION ALL
SELECT 1,3,TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'),TO_DATE('6/25/2019 10.20','MM/DD/YYYY HH24.MI'), 1000, 'Update' FROM DUAL
)
select key , new_start_date Month_Start_Date , new_end_date Month_End_Date , begin_value ,
nvl(lead(begin_value) over(order by new_start_date),begin_value) end_value
from
(
select key , new_start_date , new_end_date , sum(value) begin_value
from
(
select key, seq_num, start_date
, value, record_type ,
trunc(add_months(start_date,1),'month') new_start_date ,
trunc(add_months(start_date,2),'month')-1 new_end_date
from chg_table
where record_type = 'Insert'
)
group by key , new_start_date , new_end_date
)
order by new_start_date
;
Db Fiddle link: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=c77a71afa82769b48f424e1c0fa1c0b6
I am assuming that you are getting an "ORA-00979: not a GROUP BY expression" and this is due to your use of the TO_CHAR(timestamp_col,'DD-MM-YYYY') in the GROUP BY clause.
Adding the TO_CHAR(timestamp_col,'DD-MM-YYYY') to the select side of your statement should resolve this and provide the results you are expecting.
a, b, dateadd('day',1,last_day(timestamp_col)) start_date, TO_CHAR(timestamp_col,'DD-MM-YYYY'), ...```

Oracle select data by contiguous date blocks

I am using an Oracle database and I have a table that has 2 columns with data like so:
HASH | DATE
-----------------
abcd | 2017-11-01
abcd | 2017-11-02
abcd | 2017-11-03
wxyz | 2017-11-04
wxyz | 2017-11-05
abcd | 2017-11-06
wxyz | 2017-11-07
abcd | 2017-11-08
abcd | 2017-11-09
lmno | 2017-11-10
lmno | 2017-11-11
I want to know the windows of time that each hash is seen. So like
hash | start | end
------------------------------
abcd | 2017-11-01 | 2017-11-03
wxyz | 2017-11-04 | 2017-11-05
abcd | 2017-11-06 | 2017-11-06
wxyz | 2017-11-07 | 2017-11-07
abcd | 2017-11-08 | 2017-11-09
lmno | 2017-11-10 | 2017-11-11
What I have so far is basically this:
SELECT HASH, MIN(DATE) ST, MAX(DATE) ED
FROM HASH_TABLE
GROUP BY HASH
ORDER BY 3 DESC
And this almost works but it will give me like "abcd" as a start of 2017-11-01 and an end of 2017-11-09 which "hides" the fact that it switched in the middle.
Is there some way to group these results by contiguous date/time "blocks"?
It looks like "gaps and islands" problem:
WITH cte("hash","date") AS (
SELECT 'abcd', DATE'2017-11-01' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-02' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-03' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-04' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-05' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-06' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-07' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-08' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-09' FROM dual UNION ALL
SELECT 'lmno', DATE'2017-11-10' FROM dual UNION ALL
SELECT 'lmno', DATE'2017-11-11' FROM dual
)
select "hash"
,min("date") as startdate
,max("date") as enddate
from (
select "date","hash"
, row_number() over (order by "date")
- row_number() over (partition by "hash" order by "date") as grp
from cte
) A
group by "hash", grp
ORDER BY startdate;
DBFiddle Demo
desc hash_table
hash varchar2(4)
date_ date
select hash, min(date_) start_, max(date_) end_
from
(
select h.hash, h.date_, row_number() over (partition by hash order by date_) rn
from hash_table h
)
group by hash, date_ - rn
order by 2;

Oracle - Assigning the correct Date from a Set

I have a table A like below
REGID | PKG_DESC | EVENT_DATE | IS_CON | IS_REN
-----------------------------------------------------
1234 | cc | 27-MAR-14 | 0 | 0
1234 | cc | 27-JUN-14 | 1 | 0
1234 | GUI | 27-MAR-14 | 0 | 0
1234 | GUI | 27-JUN-14 | 1 | 0
1234 | GUI | 27-SEPT-14 | 0 | 1
1234 | GUI | 27-SEPT-15 | 0 | 1
1234 | REMOTE | 27-MAR-14 | 0 | 0
1234 | REMOTE | 27-JUN-14 | 1 | 0
1234 | REMOTE | 27-SEPT-14 | 0 | 1
2431 | cc | 27-MAR-14 | 0 | 0
2431 | cc | 27-JUN-14 | 1 | 0
I have a query like below
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_ren = 1
union
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_con = 1
Tab B contains all the usage for each reg_id there will be 100's of records.. Sample of few are
REGID | SESS_START_DT
1234 | 27-Jan-14
1234 | 20-MAR-12
1234 | 27-MAR-12
1234 | 01-sept-14
1234 | 07-sept-14
1234 | 29-JUL-14
1234 | 03-AUG-14
1234 | 27-MAR-13
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
2431 | 20-JUN-14
The Above query needs to be corrected in a way like,
1) If the REG_ID is having at least one is_ren = 1 then that subscription should be considered as renewal subscription and needs to get the 30 days and 60 days usage from table B from his is_ren = 1 event_date. (for REGID 1234 only is_ren query should execute)
2) If multiple IS_REN = 1 are existing for each REGID then the usage needs to be taken 30 days and 60 days from table B with the MIN(event_date). in this case the usage should be taken from 27-SEPT-14 instead of 27-SEPT-15
3) If there is no IS_REN = 1 and there is IS_CON = 1 then it's considered as conversion and usage should be taken before 60 days from the converted date (for REGID 2431, usage needs to get 60 days back from 27-JUN-14{this is my event_date in the query})
The O/P should be like
REGID | EVENT_DATE | DAYS 0_30 | DAYS 31-60 | CODE
1234 | 27-SEPT-14 | 2 | 2 | REN
2431 | 27-JUL-14 | 1 | 0 | CON
If my assumptions in my Comment are correct, this may be what you need. Notice the order by clause in row_number() - first the rows with is_ren = 1, then the rows with is_ren = 0 and is_con = 1, then all the other rows, and within each group order by event_date ascending. This way, the top row (rn = 1), which is the only one I use in the outer query, will have is_ren = 1 with the earliest possible date, or if no is_ren = 1 then the row with is_con = 1 and the earliest date, or else just the earliest date. (In the last case, the CODE will be null: this means there were no is_ren = 1 and no is_con = 1 for that regid.
Not sure why you have 27-JUL-14 in the output for regid = 2431, that should be 27-JUN-14. Also, there are no four-letter months in Oracle ("SEPT"). The output shows dates using my session parameters; if you need to format the dates, use to_date(event_date, .....) with the desired date format model. Also, since the data you provided is just dates (with no time-of-day component), I didn't truncate anything; you may need to, if your real data has time-of-day components.
with
table_a ( regid, pkg_desc, event_date, is_con, is_ren ) as (
select 1234, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'GUI' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-15', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'REMOTE', to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 2431, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 2431, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual
),
table_b ( regid, sess_start_dt ) as (
select 1234, to_date ('27-JAN-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('20-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('01-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('07-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('29-JUL-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('03-AUG-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-13', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 2431, to_date ('20-JUN-14', 'dd-MON-rr') from dual
),
prep ( regid, event_date, code, rn ) as (
select regid, event_date,
case when is_ren = 1 then 'REN' when is_con = 1 then 'CON' else null end,
row_number() over (partition by regid
order by case when is_ren = 1 then 0
when is_con = 1 then 1 else 2 end,
event_date)
from table_a
)
select p.regid, p.event_date,
count(case when b.sess_start_dt between p.event_date - 30 and p.event_date
then 1 end) as days_0_30,
count(case when b.sess_start_dt between p.event_date - 60 and p.event_date - 31
then 1 end) as days_31_60,
p.code
from prep p inner join table_b b on p.regid = b.regid
where rn = 1
group by p.regid, p.event_date, p.code
;
Output:
REGID EVENT_DATE DAYS_0_30 DAYS_31_60 COD
---------- ------------------- ---------- ---------- ---
1234 2014-09-27 00:00:00 2 2 REN
2431 2014-06-27 00:00:00 1 0 CON

how can use pivot on table such as datetime

I thought about writing a sql query.
I have a very simple table. There are two fields in this table.
CREATE TABLE [CHECKINOUT](
[USERID] [int] NOT NULL,
[CHECKTIME] [datetime] NOT NULL DEFAULT (getdate())
);GO
USERID CHECKTIME
1 2014-11-04 08:24:49.000
1 2014-11-03 16:57:00.000
1 2014-11-03 08:15:54.000
1 2014-10-28 12:57:58.000
1 2014-10-28 08:22:46.000
1 2014-10-24 16:58:33.000
1 2014-10-24 12:53:06.000
1 2014-10-24 08:21:38.000
1 2014-10-22 16:19:55.000
1 2014-10-21 08:26:21.000
There are sample table above.
I want to write this simple query using the pivot.
I wrote a pivot query but the value returned is null.
I wrote a query like this.
SELECT [USERID],[MORN_IN],[MORN_OUT],[NOON_IN],[NOON_OUT] FROM
(
SELECT [USERID], convert(NVARCHAR, ([CHECKTIME]), 104) as DATE_TIME FROM [CHECKINOUT]
) AS IN_OUT
PIVOT
(
MAX(DATE_TIME) --TO DATE
FOR DATE_TIME -- MY ROW COLUMN
IN
(
[MORN_IN],[MORN_OUT],[NOON_IN],[NOON_OUT] -- MY ROW COLUMN
)
) AS PIVOT_TABLE
incorrect query results--
USERID MORN_IN MORN_OUT NOON_IN NOON_OUT
1 NULL NULL NULL NULL
2 NULL NULL NULL NULL
3 NULL NULL NULL NULL
4 NULL NULL NULL NULL
5 NULL NULL NULL NULL
6 NULL NULL NULL NULL
7 NULL NULL NULL NULL
I want to do what?
the same user on the same day of their movements
I want to break into pieces.
for example:
00:00-11:00 =>MORN_IN
11:00-13:00 =>MORN_OUT(first record ONLY MIN(11:00-13:00))
12:00-15:00 =>NOON_IN (second record max(12:00-13:00) NOON_IN > MORN_OUT)
15:00-00:00 =>NOON_OUT
SELECT TOP 3 [USERID]
,[CHECKTIME]
FROM [CHECKINOUT] ORDER BY [USERID],[CHECKTIME] DESC
USERID my CHECKTIME
1 2014-10-24 16: 58: 33.000
1 2014-10-24 12: 53: 06,000
1 2014-10-24 08: 21: 38.000
now turn to the results of the pivot table (I can not do this part. but should return results like this)
USERID MORN_IN MORN_OUT NOON_IN NOON_OUT
1 2014-10-24 08: 21: 38.000 2014-10-24 12: 53: 06,000 NULL 2014-10-24 16: 58: 33.000
1
If time interval 13:00 - 16:30 is considered to be NOON_IN, then the following query:
SELECT DAY_DIVISION, [MORN_IN], [MORN_OUT], [NOON_IN], [NOON_OUT]
FROM
(SELECT CHECKTIME, CASE
WHEN CAST(CHECKTIME as time) >= '00:00:00' AND CAST(CHECKTIME as time) < '11:00:00' THEN 'MORN_IN'
WHEN CAST(CHECKTIME as time) >= '11:00:00' AND CAST(CHECKTIME as time) < '13:00:00' THEN 'MORN_OUT'
WHEN CAST(CHECKTIME as time) >= '13:00:00' AND CAST(CHECKTIME as time) < '16:30:00' THEN 'NOON_IN'
WHEN CAST(CHECKTIME as time) >= '16:30:00' THEN 'NOON_OUT'
END AS TIME_DIVISION,
RANK() OVER ( ORDER BY CAST(CHECKTIME as date) ASC) AS DAY_DIVISION
FROM CHECKINOUT) AS SourceTable
PIVOT
(
MAX(CHECKTIME)
FOR TIME_DIVISION IN ([MORN_IN], [MORN_OUT], [NOON_IN], [NOON_OUT])
) AS PivotTable;
yields this output:
DAY_DIVISION MORN_IN MORN_OUT NOON_IN NOON_OUT
------------------------------------------------------------------------------------
1 2014-10-21 08:26:21.000 NULL NULL NULL
2 NULL NULL 2014-10-22 16:19:55.000 NULL
3 2014-10-24 08:21:38.000 2014-10-24 12:54:06.000 NULL 2014-10-24 16:58:33.000
7 2014-10-28 08:22:46.000 2014-10-28 12:57:58.000 NULL NULL
9 2014-11-03 08:15:54.000 NULL NULL 2014-11-03 16:57:00.000
11 2014-11-04 08:24:49.000 NULL NULL NULL
Taking care of logins between 12:00 and 13:00 and multiple userIDs. (Tested on Oracle 11.2)
WITH
CheckInOutRaw(userID, checkTime) AS(
SELECT 1, '2014-11-04 08:24:49.000' FROM DUAL UNION ALL
SELECT 1, '2014-11-03 16:57:00.000' FROM DUAL UNION ALL
SELECT 1, '2014-11-03 08:15:54.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-28 12:57:58.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-28 08:22:46.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-24 16:58:33.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-24 12:53:06.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-24 08:21:38.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-22 16:19:55.000' FROM DUAL UNION ALL
SELECT 1, '2014-10-21 08:26:21.000' FROM DUAL UNION ALL
SELECT 2, '2014-11-04 08:24:49.000' FROM DUAL UNION ALL
SELECT 2, '2014-11-03 16:57:00.000' FROM DUAL UNION ALL
SELECT 2, '2014-11-03 08:15:54.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-29 11:07:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-29 12:07:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-29 16:57:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-28 11:07:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-28 12:07:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-28 16:57:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-28 08:22:46.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-27 12:57:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-27 12:07:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-27 16:57:58.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-27 08:22:46.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-24 16:58:33.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-24 12:53:06.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-24 08:21:38.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-22 13:19:55.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-22 16:19:55.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-21 08:26:21.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-20 12:19:55.000' FROM DUAL UNION ALL
SELECT 2, '2014-10-20 16:19:55.000' FROM DUAL
),
CheckInOutTemp AS (
SELECT
userID
, TO_TIMESTAMP(checkTime, 'YYYY-MM-DD HH24:MI:SSXFF3') checkTime
FROM
CheckInOutRaw
),
CheckInOut AS (
SELECT
userID
, checkTime
, TRUNC(checkTime) dt
, EXTRACT(HOUR FROM checkTime) h
, COUNT(CASE EXTRACT(HOUR FROM checkTime) WHEN 11 THEN 1 ELSE NULL END) OVER (PARTITION BY TRUNC(checkTime), userID) h11
, COUNT(CASE EXTRACT(HOUR FROM checkTime) WHEN 12 THEN 1 ELSE NULL END) OVER (PARTITION BY TRUNC(checkTime), userID) h12
FROM
CheckInOutTemp
),
S AS (
SELECT
userID
, checkTime
, dt
, CASE
WHEN h < 11 THEN 'MORN_IN'
WHEN h = 11 THEN 'MORN_OUT'
WHEN h11 = 1 AND h = 12 THEN 'NOON_IN'
WHEN h = 12 THEN 'MORN_OUT'
WHEN h < 16 THEN 'NOON_IN'
ELSE 'NOON_OUT'
END slot
FROM CheckInOut
WHERE NOT ((h = 12) AND (h12 = 2))
UNION ALL
SELECT
userID
, MIN(checkTime) checkTime
, dt
, 'MORN_OUT' slot
FROM CheckInOut
WHERE ((h = 12) AND (h12 = 2))
GROUP BY userID, dt
UNION ALL
SELECT
userID
, MAX(checkTime) checkTime
, dt
, 'NOON_IN' slot
FROM CheckInOut
WHERE ((h = 12) AND (h12 = 2))
GROUP BY userID, dt
)
SELECT
userID, TO_CHAR(dt, 'YYYY-MM-DD') dt, TO_CHAR(MORN_IN, 'HH24:MI:SS') morn_in, TO_CHAR(MORN_OUT, 'HH24:MI:SS') morn_out, TO_CHAR(NOON_IN, 'HH24:MI:SS') noon_in, TO_CHAR(NOON_OUT, 'HH24:MI:SS') noon_out
FROM (SELECT * FROM S PIVOT(MAX(checkTime) FOR slot IN ('MORN_IN' morn_in, 'MORN_OUT' morn_out, 'NOON_IN' noon_in, 'NOON_OUT' noon_out))) ORDER BY userID, DT DESC
;
Returns:
| USERID | DT | MORN_IN | MORN_OUT | NOON_IN | NOON_OUT |
|--------|------------|----------|----------|----------|----------|
| 1 | 2014-11-04 | 08:24:49 | (null) | (null) | (null) |
| 1 | 2014-11-03 | 08:15:54 | (null) | (null) | 16:57:00 |
| 1 | 2014-10-28 | 08:22:46 | 12:57:58 | (null) | (null) |
| 1 | 2014-10-24 | 08:21:38 | 12:53:06 | (null) | 16:58:33 |
| 1 | 2014-10-22 | (null) | (null) | (null) | 16:19:55 |
| 1 | 2014-10-21 | 08:26:21 | (null) | (null) | (null) |
| 2 | 2014-11-04 | 08:24:49 | (null) | (null) | (null) |
| 2 | 2014-11-03 | 08:15:54 | (null) | (null) | 16:57:00 |
| 2 | 2014-10-29 | (null) | 11:07:58 | 12:07:58 | 16:57:58 |
| 2 | 2014-10-28 | 08:22:46 | 11:07:58 | 12:07:58 | 16:57:58 |
| 2 | 2014-10-27 | 08:22:46 | 12:07:58 | 12:57:58 | 16:57:58 |
| 2 | 2014-10-24 | 08:21:38 | 12:53:06 | (null) | 16:58:33 |
| 2 | 2014-10-22 | (null) | (null) | 13:19:55 | 16:19:55 |
| 2 | 2014-10-21 | 08:26:21 | (null) | (null) | (null) |
| 2 | 2014-10-20 | (null) | 12:19:55 | (null) | 16:19:55 |
Looking at
| 2 | 2014-10-20 | (null) | 12:19:55 | (null) | 16:19:55 |
it does probably not make a lot of sense - but seems to be in the line with the specification.
SQL Fiddle