how can use pivot on table such as datetime - sql

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

Related

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'), ...```

SQLQuery for Time In and Time Out attendance in Oracle

I have a table in oracle with the below sample output.
EID | type | Date
24 | IN |03/25/2019 6:45 am
24 | OUT |03/25/2019 8:05 am
24 | IN |03/25/2019 8:06 am
24 | IN |03/25/2019 8:28 am
24 | OUT |03/25/2019 9:48 am
24 | IN |03/25/2019 9:52 am
24 | IN |03/25/2019 9:57 am
24 | IN |03/25/2019 10:44 am
24 | OUT |03/25/2019 12:16 pm
24 | OUT |03/25/2019 1:00 pm
24 | IN |03/25/2019 1:05 pm
24 | OUT |03/25/2019 2:21 pm
I want to build a query to achieve the below results:
EID | TIMEIN | TIMEOUT | DIIF_IN_MIN
24 | 03/25/2019 6:45 am | 03/25/2019 8:05 am | 1
24 | 03/25/2019 8:06 am | null | 0
24 | 03/25/2019 8:28 am | 03/25/2019 9:48 am | 4
24 | 03/25/2019 9:52 am | null | 0
24 | 03/25/2019 9:57 am | null | 0
24 | 03/25/2019 10:44 am | 03/25/2019 12:16 pm | 0
24 | null | 03/25/2019 1:00 pm | 5
24 | 03/25/2019 1:05 pm | 03/25/2019 2:21 pm | 0
You can use such a logic by the contribution of lead window analytic function
with tab(eid, type, dates ) as
(
select 24,'IN' ,timestamp'2019-03-25 06:45:00' from dual union all
select 24,'OUT',timestamp'2019-03-25 08:05:00' from dual union all
select 24,'IN' ,timestamp'2019-03-25 08:06:00' from dual union all
select 24,'IN' ,timestamp'2019-03-25 08:28:00' from dual union all
select 24,'OUT',timestamp'2019-03-25 09:48:00' from dual union all
select 24,'IN' ,timestamp'2019-03-25 09:52:00' from dual
)
select t1.eid, t1.dates as timein, t2.dates as timeout,
nvl(to_number(regexp_substr(to_char(t1.ld_dates - t2.dates),'[^:]+',1,2)),0)
as diff_in_minutes
from ( select lead(dates) over (order by dates) as ld_dates, t.*
from tab t
where type = 'IN' order by dates) t1
full join ( select * from tab where type = 'OUT' order by dates) t2
on t1.dates <= t2.dates and ld_dates > t2.dates
order by t1.dates;
EID TIMEIN TIMEOUT DIFF_IN_MINUTES
24 25.03.2019 06:45:00 25.03.2019 08:05:00 1
24 25.03.2019 08:06:00 NULL 0
24 25.03.2019 08:28:00 25.03.2019 09:48:00 4
24 25.03.2019 09:52:00 NULL 0
Demo
You can do this with the following logic.
You can get all the ins using a lead() query. Then you can get the unmatched outs using a lag():
select t.eid, date as timein,
(case when next_type = 'OUT' then next_date end) as timeout,
((case when next_type = 'OUT' then next_date end) - date) * (24 * 60) as diff_in_minutes
from (select t.*,
lead(type) over (partition by eid order by date) as next_type,
lead(type) over (partition by eid order by date) as next_date
from t
) t
where type = 'IN'
union all
select t.eid, null as timein,
date as timeout, null as diff_in_minutes
from (select t.*,
lag(type) over (partition by eid order by date) as prev_type,
lag(date) over (partition by eid order by date) as prev_date
from t
) t
where type = 'OUT' and (prev_type <> 'IN' or prev_type is null);
Here is a db<>fiddle with all your data, showing that it supports the multiple INs and OUTs.
Note this assumes that the date/time column is really a date. It only converts to a timestamp to show the time component in the result set.

Deriving continuous history from overlapping from/to periods, with gaps [duplicate]

I have a table with 200.000 rows in a SQL Server 2014 database looking like this:
CREATE TABLE DateRanges
(
Contract VARCHAR(8),
Sector VARCHAR(8),
StartDate DATE,
EndDate DATE
);
INSERT INTO DateRanges (Contract, Sector, StartDate, Enddate)
SELECT '111', '999', '01-01-2014', '03-31-2014'
union
SELECT '111', '999', '04-01-2014', '06-30-2014'
union
SELECT '111', '999', '07-01-2014', '09-30-2014'
union
SELECT '111', '999', '10-01-2014', '12-31-2014'
union
SELECT '111', '888', '08-01-2014', '08-31-2014'
union
SELECT '111', '777', '08-15-2014', '08-31-2014'
union
SELECT '222', '999', '01-01-2014', '03-31-2014'
union
SELECT '222', '999', '04-01-2014', '06-30-2014'
union
SELECT '222', '999', '07-01-2014', '09-30-2014'
union
SELECT '222', '999', '10-01-2014', '12-31-2014'
union
SELECT '222', '666', '11-01-2014', '11-30-2014'
UNION
SELECT '222', '555', '11-15-2014', '11-30-2014';
As you can see there can be multiple overlaps for each contract and what I would like to have is the result like this
Contract Sector StartDate EndDate
---------------------------------------------
111 999 01-01-2014 07-31-2014
111 888 08-01-2014 08-14-2014
111 777 08-15-2014 08-31-2014
111 999 09-01-2014 12-31-2014
222 999 01-01-2014 10-31-2014
222 666 11-01-2014 11-14-2014
222 555 11-15-2014 11-30-2014
222 999 12-01-2014 12-31-2014
I can not figure out how this can be done and the examples i have seen on this site quite do not fit my problem.
This answer makes use of a few different techniques. The first is a recursive-cte that creates a table with every relevant cal_date which then gets cross apply'd with unique Contract values to get every combination of both values. The second is window-functions such as lag and row_number to determine a variety of things detailed in the comments below. Lastly, and probably most importantly, gaps-and-islands to determine when one Contract/Sector combination ends and the next begins.
Answer:
--determine range of dates
declare #bgn_dt date = (select min(StartDate) from DateRanges)
, #end_dt date = (select max(EndDate) from DateRanges)
--use a recursive CTE to create a record for each day / Contract
; with dates as
(
select #bgn_dt as cal_date
union all
select dateadd(d, 1, a.cal_date) as cal_date
from dates as a
where a.cal_date < #end_dt
)
select d.cal_date
, c.Contract
into #contract_dates
from dates as d
cross apply (select distinct Contract from DateRanges) as c
option (maxrecursion 0)
--Final Select
select f.Contract
, f.Sector
, min(f.cal_date) as StartDate
, max(f.cal_date) as EndDate
from (
--Use the sum-over to obtain the Island Numbers
select dr.Contract
, dr.Sector
, dr.cal_date
, sum(dr.IslandBegin) over (partition by dr.Contract order by dr.cal_date asc) as IslandNbr
from (
--Determine if the record is the start of a new Island
select a.Contract
, a.Sector
, a.cal_date
, case when lag(a.Sector, 1, NULL) over (partition by a.Contract order by a.cal_date asc) = a.Sector then 0 else 1 end as IslandBegin
from (
--Determine which Contract/Date combinations are valid, and rank the Sectors that are in effect
select cd.cal_date
, dr.Contract
, dr.Sector
, dr.EndDate
, row_number() over (partition by dr.Contract, cd.cal_date order by dr.StartDate desc) as ConractSectorRnk
from #contract_dates as cd
left join DateRanges as dr on cd.Contract = dr.Contract
and cd.cal_date between dr.StartDate and dr.EndDate
) as a
where a.ConractSectorRnk = 1
and a.Contract is not null
) as dr
) as f
group by f.Contract
, f.Sector
, f.IslandNbr
order by f.Contract asc
, min(f.cal_date) asc
Output:
+----------+--------+------------+------------+
| Contract | Sector | StartDate | EndDate |
+----------+--------+------------+------------+
| 111 | 999 | 2014-01-01 | 2014-07-31 |
| 111 | 888 | 2014-08-01 | 2014-08-14 |
| 111 | 777 | 2014-08-15 | 2014-08-31 |
| 111 | 999 | 2014-09-01 | 2014-12-31 |
| 222 | 999 | 2014-01-01 | 2014-10-31 |
| 222 | 666 | 2014-11-01 | 2014-11-14 |
| 222 | 555 | 2014-11-15 | 2014-11-30 |
| 222 | 999 | 2014-12-01 | 2014-12-31 |
+----------+--------+------------+------------+

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 I select only newest entries?

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)*