ORACLE GROUP BY with Date does not group correctly - oracle

I try to group a given table by date to get a min and max date of member IDs. The result should display a time range from when to when a member was part of an OE.
my given table (excerpt):
ID DATE OE
11 2021-03-06 00:00:00 2926
11 2021-03-07 00:00:00 3879
11 2021-03-08 00:00:00 3879
11 2021-03-09 00:00:00 3879
11 2021-03-10 00:00:00 2926
11 2021-03-11 00:00:00 2926
11 2021-03-12 00:00:00 2926
11 2021-03-13 00:00:00 2926
11 2021-03-14 00:00:00 2926
11 2021-03-15 00:00:00 2926
11 2021-03-16 00:00:00 1344
11 2021-03-17 00:00:00 1344
11 2021-03-18 00:00:00 1344
11 2021-03-19 00:00:00 1344
11 2021-03-20 00:00:00 1344
11 2021-03-21 00:00:00 1344
11 2021-03-22 00:00:00 2926
11 2021-03-23 00:00:00 2926
11 2021-03-24 00:00:00 2926
11 2021-03-25 00:00:00 2926
11 2021-03-26 00:00:00 2926
11 2021-03-27 00:00:00 2926
11 2021-03-28 00:00:00 2926
11 2021-03-29 00:00:00 2926
11 2021-03-30 00:00:00 2926
11 2021-03-31 00:00:00 2926
11 2021-04-01 00:00:00 1549
11 2021-04-02 00:00:00 1549
11 2021-04-03 00:00:00 1549
11 2021-04-04 00:00:00 2926
My Select:
select id, min(date) as mind, max(date) as maxd,OE
from <table>
group by id,oe
order by mind desc;
The output should be something like this:
ID | MIND | MAXD | OE
11 2021-04-04 00:00:00 2021-04-04 00:00:00 2926
11 2021-04-01 00:00:00 2021-04-03 00:00:00 1549
11 2021-03-22 00:00:00 2021-03-31 00:00:00 2926
11 2021-03-16 00:00:00 2021-03-21 00:00:00 1344
11 2021-03-10 00:00:00 2021-03-15 00:00:00 2926
11 2021-03-07 00:00:00 2021-03-09 00:00:00 3879
11 2021-03-06 00:00:00 2021-03-06 00:00:00 2926
But it is like this:
ID | MIND | MAXD | OE
11 2021-04-01 00:00:00 2021-04-03 00:00:00 1549
11 2021-03-16 00:00:00 2021-03-21 00:00:00 1344
11 2021-03-07 00:00:00 2021-03-09 00:00:00 3879
11 2021-03-06 00:00:00 2021-04-04 00:00:00 2926
The result should display a time range from when to when a member was part of an OE. Even if I add some other IDs (which I obviously have) it does not show the timeline of OE changes in correct order.
Any help highly appreciated!
TIA,
Michael

You can use a trick called tabibitosan to do this kind of grouping:
SELECT id,
MIN(dt) AS mind,
MAX(dt) AS maxd,
oe
FROM (SELECT id,
dt,
oe,
row_number() OVER (PARTITION BY ID ORDER BY dt) - row_number() OVER (PARTITION BY ID, oe ORDER BY dt) grp
FROM your_table)
GROUP BY id,
oe,
grp
ORDER BY mind DESC;
See this dbfiddle for results
This works by assigning row numbers across the whole set of data (in this case, it's across each id), and then finding the row numbers across the subsets of data (i.e. across each id and oe), and then subtracting one from the other to form a number you can group by. Consecutive rows get the same group number, but every time there's a non-consecutive row, the group number will change.

From Oracle 12, you can use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY "DATE"
MEASURES
FIRST(oe) AS oe,
FIRST("DATE") AS mind,
LAST("DATE") AS maxd
PATTERN (same_oe+)
DEFINE
same_oe AS oe = FIRST(oe)
)
Which, for the sample data:
CREATE TABLE table_name (ID, "DATE", OE) AS
SELECT 11, DATE '2021-03-06', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-07', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-08', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-09', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-10', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-11', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-12', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-13', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-14', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-15', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-16', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-17', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-18', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-19', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-20', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-21', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-22', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-23', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-24', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-25', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-26', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-27', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-28', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-29', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-30', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-31', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-01', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-02', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-03', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-04', 2926 FROM DUAL;
Outputs:
ID
OE
MIND
MAXD
11
2926
2021-03-06 00:00:00
2021-03-06 00:00:00
11
3879
2021-03-07 00:00:00
2021-03-09 00:00:00
11
2926
2021-03-10 00:00:00
2021-03-15 00:00:00
11
1344
2021-03-16 00:00:00
2021-03-21 00:00:00
11
2926
2021-03-22 00:00:00
2021-03-31 00:00:00
11
1549
2021-04-01 00:00:00
2021-04-03 00:00:00
11
2926
2021-04-04 00:00:00
2021-04-04 00:00:00
fiddle

Related

Oracle How to get the birthday list from last 15 days to next 15 days?

I've a table with employees and their birth date, in a column in a format string.
I cannot modify the table, so I created a view to get their birth date in a real date format (TO_DATE).
Now, I would like to get the list of the employees having theirs birthday in the last 15 days and the employees who'll have theirs birthday in the next 15 days.
So, just based with the Day and the month.
I successfully get for exemple all employees bornt in April with "Extract", but, I'm sure you've already understand, when I'll run the query the 25 April, I'd like the futures birthday in May.
How could I get that (oracle 12c)
Thank you 🙂
Using the hiredate column in table scott.emp for testing:
select empno, ename, hiredate
from scott.emp
where add_months(trunc(hiredate),
12 * round(months_between(sysdate, hiredate) / 12))
between trunc(sysdate) - 15 and trunc(sysdate) + 15
;
EMPNO ENAME HIREDATE
---------- ---------- ----------
7566 JONES 04/02/1981
7698 BLAKE 05/01/1981
7788 SCOTT 04/19/1987
This will produce the wrong result in the following situation: if someone's birthday is Feb. 28 in a non-leap year, their birthday in a leap year (calculated with the ADD_MONTHS function in the query) will be considered to be Feb. 29. So, they will be excluded if running the query on, say, Feb. 13 2024 (even though they should be included), and they will be included if running the query on March 14 (even though they should be excluded). If you can live with this - those people will be recognized in the wrong window, once every four years - then this may be all you need. Otherwise that situation will require further tweaking.
For people born on Feb. 29 (in a leap year, obviously), their birthday in a non-leap-year is considered to be Feb. 28. With this convention, the query will always work correctly for them. Whether this convention is appropriate in your locale, only your business users can tell you. (Local laws and regulations may matter, too - depending on what you are using this for.)
You can use ddd format model:
DDD - Day of year (1-366).
For example:
SQL> with v(dt) as (
2 select date'2020-01-01'+level-1 from dual connect by date'2020-01-01'+level-1<date'2021-01-01'
3 )
4 select *
5 from v
6 where
7 not abs(
8 to_number(to_char(date'&dt','ddd'))
9 -to_number(to_char(dt ,'ddd'))
10 ) between 15 and 350;
Enter value for dt: 2022-01-03
DT
-------------------
2020-01-01 00:00:00
2020-01-02 00:00:00
2020-01-03 00:00:00
2020-01-04 00:00:00
2020-01-05 00:00:00
2020-01-06 00:00:00
2020-01-07 00:00:00
2020-01-08 00:00:00
2020-01-09 00:00:00
2020-01-10 00:00:00
2020-01-11 00:00:00
2020-01-12 00:00:00
2020-01-13 00:00:00
2020-01-14 00:00:00
2020-01-15 00:00:00
2020-01-16 00:00:00
2020-01-17 00:00:00
2020-12-19 00:00:00
2020-12-20 00:00:00
2020-12-21 00:00:00
2020-12-22 00:00:00
2020-12-23 00:00:00
2020-12-24 00:00:00
2020-12-25 00:00:00
2020-12-26 00:00:00
2020-12-27 00:00:00
2020-12-28 00:00:00
2020-12-29 00:00:00
2020-12-30 00:00:00
2020-12-31 00:00:00
30 rows selected.
NB: This example doesn't analyze leap years.
Similar to mathguy's answer, but translating the current date back to the birth year (rather than translating the birth year forwards):
SELECT *
FROM employees
WHERE birth_date BETWEEN ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) - INTERVAL '15' DAY
AND ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) + INTERVAL '15' DAY;
Then, for the sample data:
CREATE TABLE employees (name, birth_date) AS
SELECT 'Alice', DATE '2020-02-28' FROM DUAL UNION ALL
SELECT 'Betty', DATE '2020-02-29' FROM DUAL UNION ALL
SELECT 'Carol', DATE '2021-02-28' FROM DUAL UNION ALL
SELECT 'Debra', DATE '2022-04-28' FROM DUAL UNION ALL
SELECT 'Emily', DATE '2021-03-30' FROM DUAL UNION ALL
SELECT 'Fiona', DATE '2021-03-31' FROM DUAL;
If today's date is 2022-04-16 then the output is:
NAME
BIRTH_DATE
Debra
28-APR-22
If today's date is 2022-03-15 then the output is:
NAME
BIRTH_DATE
Betty
29-FEB-20
Carol
28-FEB-21
Emily
30-MAR-21
And would get values from 28th February - 30th March in a non-leap-year and from 29th February - 30th March in a leap year.
db<>fiddle here

How to query date differences between dates of same column?

I have a table with a date field and I need a query to return the ID of records that are on a certain day.
Example
ID UPDATED_DATE
42 31-DEC-19 12.00.00.000000000 AM
43 24-DEC-19 12.00.00.000000000 AM
44 03-DEC-19 12.00.00.000000000 AM
45 18-NOV-19 12.00.00.000000000 AM
46 08-NOV-19 12.00.00.000000000 AM
47 01-NOV-19 12.00.00.000000000 AM
48 26-OCT-19 12.00.00.000000000 AM
49 04-OCT-19 12.00.00.000000000 AM
50 20-SEP-19 12.00.00.000000000 AM
I need a query to find if the DAY part of the date is >= 1 and < 5.
In the example, I will get the ID as 44, 47 and 49 as output.
Can anyone help me for the query, please?
Use TO_CHAR with appropriate format mask:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with test as
2 (select 42 id, date '2019-12-31' updated_date from dual union all
3 select 43, date '2019-12-24' from dual union all
4 select 44, date '2019-12-03' from dual union all
5 select 45, date '2019-11-18' from dual union all
6 select 46, date '2019-11-08' from dual union all
7 select 47, date '2019-11-01' from dual union all
8 select 48, date '2019-10-26' from dual union all
9 select 49, date '2019-10-04' from dual union all
10 select 50, date '2019-09-20' from dual
11 )
12 select id, updated_date, to_char(updated_date, 'fmdd') dd
13 from test
14 where to_char(updated_date, 'fmdd') >= 1
15 and to_char(updated_date, 'fmdd') < 5
16 order by id;
ID UPDATED_DA DD
---------- ---------- --
44 03.12.2019 3
47 01.11.2019 1
49 04.10.2019 4
SQL>
If you want the difference (as per the original version of the question) between day-of-month of two rows to be between 1 and 4 days then use LAG/LEAD:
SELECT ID,
UPDATED_DATE
FROM (
SELECT ID,
UPDATED_DATE,
EXTRACT(DAY FROM updated_date) AS day,
LAG( EXTRACT(DAY FROM updated_date) )
OVER ( ORDER BY EXTRACT(DAY FROM updated_date) ) AS prev_day,
LEAD( EXTRACT(DAY FROM updated_date) )
OVER ( ORDER BY EXTRACT(DAY FROM updated_date) ) AS next_day
FROM table_name
)
WHERE day - prev_day BETWEEN 1 AND 4
OR next_day - day BETWEEN 1 AND 4
Which for your test data ouptuts:
ID | UPDATED_DATE
-: | :--------------------
47 | 01-NOV-19 12:00:00 AM
44 | 03-DEC-19 12:00:00 AM
49 | 04-OCT-19 12:00:00 AM
46 | 08-NOV-19 12:00:00 AM
45 | 18-NOV-19 12:00:00 AM
50 | 20-SEP-19 12:00:00 AM
43 | 24-DEC-19 12:00:00 AM
48 | 26-OCT-19 12:00:00 AM
If you want the day-of-month to be between 1 and 4 then use EXTRACT:
SELECT *
FROM table_name
WHERE EXTRACT(DAY FROM updated_date) BETWEEN 1 AND 4
Which outputs:
ID | UPDATED_DATE
-: | :--------------------
44 | 03-DEC-19 12:00:00 AM
47 | 01-NOV-19 12:00:00 AM
49 | 04-OCT-19 12:00:00 AM
db<>fiddle here

Oracle query for getting particular Date

I have an oracle query which returns next mont first sunday.
now i have a condition here to check if the date which has passed is first sunday of current month, then we need second sunday in current month.
Else, next month first sunday.
My query:
DEF startDate = to_date('somedate', 'dd/mm/yyyy');
Select next_day(last_day(&startDate),'Sunday' ) from dual ;
Expected output:
if we input 1st july 2018, it has to return 8th july 2018(second sunday) else, any other day apart from first sunday like, (2nd july 2018), it has to return 5th Aug 2018.
Input Expected Output
01-Jul-18 08-Jul-18,
02-Jul-18 05-Aug-18,
05-Aug-18 12-Aug-18,
19-Aug-18 02-Sep-18.
Based on the description in your question and comments you want something like:
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday') -- date is on first sunday
then next_day(start_date, 'Sunday') -- next Sunday, which is second in month
else next_day(last_day(start_date), 'Sunday') -- first Sunday of next month
end
With some sample dates in a CTE, including some discussed but also others:
with cte (start_date) as (
select date '2018-05-30' from dual
union all select date '2018-06-01' from dual
union all select date '2018-06-02' from dual
union all select date '2018-06-03' from dual
union all select date '2018-06-04' from dual
union all select date '2018-06-30' from dual
union all select date '2018-07-01' from dual
union all select date '2018-07-02' from dual
union all select date '2018-07-03' from dual
union all select date '2018-07-04' from dual
union all select date '2018-07-05' from dual
union all select date '2018-07-06' from dual
union all select date '2018-07-07' from dual
union all select date '2018-07-08' from dual
union all select date '2018-07-31' from dual
union all select date '2018-08-01' from dual
union all select date '2018-08-02' from dual
union all select date '2018-08-03' from dual
union all select date '2018-08-04' from dual
union all select date '2018-08-05' from dual
union all select date '2018-08-06' from dual
)
select start_date,
to_char(start_date, 'Dy') as day,
case when to_char(start_date, 'Dy') = 'Sun'
then 'Yes' else 'No' end as is_sunday,
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday')
then 'Yes' else 'No' end as is_first_sunday,
next_day(trunc(start_date, 'MM') - 1, 'Sunday') as first_sun_this_month,
next_day(trunc(start_date, 'MM') + 6, 'Sunday') as second_sun_this_month,
next_day(last_day(start_date), 'Sunday') as first_sun_next_month,
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday') -- date is on first sunday
then next_day(start_date, 'Sunday') -- next Sunday, which is second in month
else next_day(last_day(start_date), 'Sunday') -- first Sunday of next month
end as result
from cte;
gets
START_DATE DAY IS_ IS_ FIRST_SUN_ SECOND_SUN FIRST_SUN_ RESULT
---------- ------------ --- --- ---------- ---------- ---------- ----------
2018-05-30 Wed No No 2018-05-06 2018-05-13 2018-06-03 2018-06-03
2018-06-01 Fri No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-02 Sat No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-03 Sun Yes Yes 2018-06-03 2018-06-10 2018-07-01 2018-06-10
2018-06-04 Mon No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-30 Sat No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-07-01 Sun Yes Yes 2018-07-01 2018-07-08 2018-08-05 2018-07-08
2018-07-02 Mon No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-03 Tue No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-04 Wed No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-05 Thu No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-06 Fri No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-07 Sat No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-08 Sun Yes No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-31 Tue No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-08-01 Wed No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-02 Thu No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-03 Fri No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-04 Sat No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-05 Sun Yes Yes 2018-08-05 2018-08-12 2018-09-02 2018-08-12
2018-08-06 Mon No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
The result column is the one you're interested in, the others just try to show the working a bit.
It looks odd to have the result dates out of sequence compared to the input dates - 2018-06-03 going to 2018--06-10 then those both before and after it go to 2018-07-01. But that seems to be what you want.

To find how many questions answered by number of user in an hour

I have a requirement to show 1 to 20 questions how many teams answered for 0-23 hours. The table structure
Team_id Question_id Updated
124 25092 02-SEP-14 11:30:12 AM
15 25076 02-SEP-14 02:31:15 PM
258 25061 02-SEP-14 03:02:33 PM
298 25196 02-SEP-14 03:32:49 PM
450 25203 02-SEP-14 07:01:05 PM
471 25351 02-SEP-14 07:47:31 PM
482 25350 02-SEP-14 08:01:03 PM
78 24924 02-SEP-14 08:01:29 PM
541 26032 02-SEP-14 09:35:00 PM
708 26485 02-SEP-14 12:19:48 PM
726 26125 02-SEP-14 01:00:11 PM
145 26221 02-SEP-14 03:45:32 PM
824 26436 02-SEP-14 06:30:16 PM
87 26505 02-SEP-14 06:30:22 PM
825 26488 02-SEP-14 06:31:34 PM
466 26488 02-SEP-14 06:34:48 PM
868 26091 02-SEP-14 08:30:59 PM
1356 28852 02-SEP-14 07:45:14 PM
1342 28852 02-SEP-14 07:45:27 PM
Query should return
Hours 1 2 3 4 5 to 20
10 6 0 2 3 6 to 25
11 3 2 1 1 7 to 300
12 2 0 5 0 7 to 30
13 1 6 1 1 7 to 40
these are count of team that answered only 1 question in hour 10
these are count of team that answered only 2 question in hour 10 etc.
Note it is not 1st or 2nd question.
Thanks
Arun
You need something like this I suppose:
select hour,
count(decode(cnt, 1, 1)) as q1,
count(decode(cnt, 2, 1)) as q2,
count(decode(cnt, 3, 1)) as q3,
count(decode(cnt, 4, 1)) as q4,
...
count(decode(cnt, 20, 1)) as q20
from (
select to_char(updated, 'HH24') as hour
, count(*) as cnt
from &table_name
group by to_char(updated, 'HH24'), team_id
)
group by hour;
As far as I understand, perhaps you want something like this :
SQL> WITH DATA AS(
2 SELECT 124 team_id, 25092 question_id, to_date('02-SEP-14 11:30:12 AM','DD-MON-RR HH:MI:SS AM') UPDATED FROM DUAL UNION ALL
3 SELECT 15 , 25076 ,to_date('02-SEP-14 02:31:15 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
4 SELECT 258 , 25061 ,to_date('02-SEP-14 03:02:33 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
5 SELECT 298 , 25196 ,to_date('02-SEP-14 03:32:49 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
6 SELECT 450 , 25203 ,to_date('02-SEP-14 07:01:05 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
7 SELECT 471 , 25351 ,to_date('02-SEP-14 07:47:31 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
8 SELECT 482 , 25350 ,to_date('02-SEP-14 08:01:03 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
9 SELECT 78 , 24924 ,to_date('02-SEP-14 08:01:29 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
10 SELECT 541 , 26032 ,to_date('02-SEP-14 09:35:00 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
11 SELECT 708 , 26485 ,to_date('02-SEP-14 12:19:48 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
12 SELECT 726 , 26125 ,to_date('02-SEP-14 01:00:11 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
13 SELECT 145 , 26221 ,to_date('02-SEP-14 03:45:32 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
14 SELECT 824 , 26436 ,to_date('02-SEP-14 06:30:16 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
15 SELECT 87 , 26505 ,to_date('02-SEP-14 06:30:22 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
16 SELECT 825 , 26488 ,to_date('02-SEP-14 06:31:34 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
17 SELECT 466 , 26488 ,to_date('02-SEP-14 06:34:48 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
18 SELECT 868 , 26091 ,to_date('02-SEP-14 08:30:59 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
19 SELECT 1356 , 28852 ,to_date('02-SEP-14 07:45:14 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL UNION ALL
20 SELECT 1342 , 28852 ,to_date('02-SEP-14 07:45:27 PM','DD-MON-RR HH:MI:SS AM') FROM DUAL)
21 SELECT 5 hours,
22 COUNT(team_id)
23 FROM DATA
24 WHERE UPDATED BETWEEN to_date('02-SEP-14 10:00:01','DD-MON-RR HH24:MI:SS') AND to_date('02-SEP-14 14:59:59','DD-MON-RR HH24:MI:SS')
25 UNION
26 SELECT 6 hours,
27 COUNT(team_id)
28 FROM DATA
29 WHERE UPDATED BETWEEN to_date('02-SEP-14 10:00:01','DD-MON-RR HH24:MI:SS') AND to_date('02-SEP-14 15:59:59','DD-MON-RR HH24:MI:SS')
30 UNION
31 SELECT 10 hours,
32 COUNT(team_id)
33 FROM DATA
34 WHERE UPDATED BETWEEN to_date('02-SEP-14 10:00:01','DD-MON-RR HH24:MI:SS') AND to_date('02-SEP-14 19:59:59','DD-MON-RR HH24:MI:SS')
35 UNION
36 SELECT 24 hours,
37 COUNT(team_id)
38 FROM DATA
39 WHERE UPDATED BETWEEN to_date('02-SEP-14 00:00:01','DD-MON-RR HH24:MI:SS') AND to_date('02-SEP-14 23:59:59','DD-MON-RR HH24:MI:SS')
40 /
HOURS COUNT(TEAM_ID)
---------- --------------
5 4
6 7
10 15
24 19
The time interval as an example, I took varying intervals, and the HOURS column is the time interval mentioned in the WHERE clause.

Convert columns to rows in oracle [duplicate]

This question already has answers here:
Oracle SQL pivot query
(4 answers)
Closed 8 years ago.
this is my table in oracle 11g:
**date qty1 qty2 qty3 qty4**
2-Feb-14 61 64 52 54
2-Mar-14 124 130 149 156
i want to convert it into the following table. i.e. add 7 days to the date and transpose the qty. And i have till qty52 such metrics
***date qty***
**2-Feb-14 61**
9-Feb-14 64
16-Feb-14 52
23-Feb-14 54
**2-Mar-14 124**
9-Mar-14 130
16-Mar-14 149
23-Mar-14 156
have a try:
WITH t(my_date, val, val2, val3, val4)
AS (
SELECT to_date('01/01/2014 12:00:00 AM', 'dd/mm/yyyy hh:mi:ss am'), 1,2,3,4 from dual
UNION ALL
SELECT to_date('01/02/2014 12:00:00 AM', 'dd/mm/yyyy hh:mi:ss am'), 5,6,7,8 FROM dual
)
SELECT (my_date-7) + (row_number() OVER (partition by my_date ORDER BY my_date)*7) my_date, value as qty
FROM (
( SELECT my_date, val, val2, val3, val4 FROM t
) unpivot ( value FOR value_type IN (val, val2, val3, val4) ) );
output:
MY_DATE QTY
----------------------- ----------
01/01/2014 12:00:00 AM 1
08/01/2014 12:00:00 AM 2
15/01/2014 12:00:00 AM 3
22/01/2014 12:00:00 AM 4
01/02/2014 12:00:00 AM 5
08/02/2014 12:00:00 AM 6
15/02/2014 12:00:00 AM 7
22/02/2014 12:00:00 AM 8
select date,qty from
(select date,qty1 as qty
from tbl
union
select date+7 as date,qty2 as qty
from tbl
union
select date+14 as date,qty3 as qty
from tbl
union
select date+21 as date,qty4 as qty
from tbl)
order by date
If you've got Oracle 11g, I'd look at doing it with UNPIVOT.
select
start_date + to_number(week_number) * 7,
qty
from (
select *
from quantity_data
unpivot (qty for week_number
in (qty1 as '0', qty2 as '1', qty3 as '2', qty4 as '3'))
)
This is an alternative to the example from ajmalmhd04, using to_number instead of the row_number analytic function. The answer from ajmalmhd04 is probably more generic though
If you haven't got Oracle 11g then try this for an option:
with pivot_data as (
select 0 as pivot_col from dual union all
select 1 from dual union all
select 2 from dual union all
select 3 from dual
)
select
start_date + (7 * pivot_col) as start_date,
case
when pivot_col = 0 then qty1
when pivot_col = 1 then qty2
when pivot_col = 2 then qty3
when pivot_col = 3 then qty4 end as qty
from
quantity_data cross join pivot_data
order by 1
Try this
with tab(date_d,qty1,qty2,qty3,qty4) as (
select '2-Feb-14',61,64,52,54 from dual union all
select '2-Mar-14',124,130,149,156 from dual),
tab2(dd, ss) as (select date_d, qty1||','||qty2||','||qty3||','||qty4 from tab)
select to_date(dd) + ((level-1) * 7) "DATE", regexp_substr(ss, '[^(,)]+', 1, level) "QTY"
from tab2
connect by level <= length(ss) - length(replace(ss, ',')) + 1
and prior ss = ss
and prior sys_guid() is not null
output
| DATE | QTY |
|---------------------------------|-----|
| March, 02 2014 00:00:00+0000 | 124 |
| March, 09 2014 00:00:00+0000 | 130 |
| March, 16 2014 00:00:00+0000 | 149 |
| March, 23 2014 00:00:00+0000 | 156 |
| February, 02 2014 00:00:00+0000 | 61 |
| February, 09 2014 00:00:00+0000 | 64 |
| February, 16 2014 00:00:00+0000 | 52 |
| February, 23 2014 00:00:00+0000 | 54 |
Let me know if it meets your requirement.

Resources