Get month wise yearly report in oracle - oracle

all I have a employee table with the following fields employee name, wages date, wages I want to sum records month-wise.here is table data. I m using oracle database 11g
and here is the output I want.

You can use ROLLUP in your GROUP BY
WITH t(NAME, dt, w) AS (
SELECT 'adam', DATE '2020-01-01', 200 FROM dual UNION ALL
SELECT 'adam', DATE '2020-02-01', 200 FROM dual UNION ALL
SELECT 'adam', DATE '2020-03-01', 200 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-01-01', 100 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-02-01', 200 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-03-01', 151 FROM dual
)
SELECT NAME, NVL(TO_CHAR(dt, 'fmMon'), 'total') AS mon, SUM(w) AS sum_w
FROM t
GROUP BY NAME, ROLLUP(TO_CHAR(dt, 'fmMon'));
+-----------------+
|NAME |MON |SUM_W|
+-----------------+
|adam |Feb |200 |
|adam |Jan |200 |
|adam |Mar |200 |
|adam |total|600 |
|jhone|Feb |200 |
|jhone|Jan |100 |
|jhone|Mar |151 |
|jhone|total|451 |
+-----------------+
If you need to transpose your result, you can PIVOT it:
WITH t(NAME, dt, w) AS (
SELECT 'adam', DATE '2020-01-01', 200 FROM dual UNION ALL
SELECT 'adam', DATE '2020-02-01', 200 FROM dual UNION ALL
SELECT 'adam', DATE '2020-03-01', 200 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-01-01', 100 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-02-01', 200 FROM dual UNION ALL
SELECT 'jhone', DATE '2020-03-01', 151 FROM dual
)
SELECT *
FROM (
SELECT NAME, NVL(TO_CHAR(dt, 'fmMon'), 'total') AS mon, SUM(w) AS sum_w
FROM t
GROUP BY NAME, ROLLUP(TO_CHAR(dt, 'fmMon'))
)
PIVOT (
SUM(sum_w)
FOR mon IN ('Jan','Feb','Mar','total')
);
+-------------------------------+
|NAME |'Jan'|'Feb'|'Mar'|'total'|
+-------------------------------+
|adam |200 |200 |200 |600 |
|jhone|100 |200 |151 |451 |
+-------------------------------+

You can use conditional aggregation as follows:
Select name,
Sum(case when to_char(date,'mon') = 'jan' then wages end) as jan,
Sum(case when to_char(date,'mon') = 'feb' then wages end) as feb,
...
Sum(wages) as total
From yourTable
Group by name;
You need to use the where condition to only consider one year data.

Related

ROW_NUMBER over PARTITION BY restart row counter between breaks [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a list of activities that is currently ordered by user, date and time of activity, and ID. I want to generate numbers for each group set by those same fields. Using the following code, I achieve considerable accuracy. However, there's a problem when the same ID is repeated at a later time and I need the row number count to restart instead of continuing from the previous iteration.
Here's my code:
ROW_NUMBER() OVER (PARTITION BY USER_ID, foc_id ORDER BY USER_ID, to_char(activity_date, 'MM/DD/YYYY HH24:MI:SS'), foc_id) seq_nbr
In the image below, we see that FOC_ID "A240" had activity around 2:20PM. Then FOC_ID "B410" had activity around 3:19PM, lastly the user returned to "A240" for additional activity around 3:20. Because there was activity between the first and second sequence of events of "A240," I need the row number (seq_nbr) to restart instead of continuing from the previous activity.
You can use MATCH_RECOGNIZE:
SELECT user_id,
activity_date,
foc_id,
ROW_NUMBER() OVER ( PARTITION BY user_id, mno ORDER BY activity_date ) AS seq_num
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY user_id
ORDER BY activity_date
MEASURES
MATCH_NUMBER() AS mno
ALL ROWS PER MATCH
PATTERN ( same_foc_id* last_row )
DEFINE
same_foc_id AS FIRST( foc_id ) = NEXT( foc_id )
)
or, multiple ROW_NUMBERs:
SELECT user_id,
activity_date,
foc_id,
ROW_NUMBER() OVER ( PARTITION BY user_id, foc_id, grp ORDER BY activity_date ) AS seq_num
FROM (
SELECT user_id,
activity_date,
foc_id,
ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY activity_date )
- ROW_NUMBER() OVER ( PARTITION BY user_id, foc_id ORDER BY activity_date ) AS grp
FROM table_name
)
ORDER BY user_id, activity_date
Which, for the sample data:
CREATE TABLE table_name ( user_id, activity_date, foc_id ) AS
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '14:20:34' HOUR TO SECOND, 'A240' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '14:21:23' HOUR TO SECOND, 'A240' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '14:21:23' HOUR TO SECOND, 'A240' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '14:21:23' HOUR TO SECOND, 'A240' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:19:39' HOUR TO SECOND, 'B410' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:19:44' HOUR TO SECOND, 'B410' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:19:58' HOUR TO SECOND, 'B410' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:20:11' HOUR TO SECOND, 'B410' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:22:16' HOUR TO SECOND, 'A240' FROM DUAL UNION ALL
SELECT 'UVAC3', DATE '2020-11-04' + INTERVAL '15:22:33' HOUR TO SECOND, 'A240' FROM DUAL;
Both output:
USER_ID | ACTIVITY_DATE | FOC_ID | SEQ_NUM
:------ | :------------------ | :----- | ------:
UVAC3 | 2020-11-04 14:20:34 | A240 | 1
UVAC3 | 2020-11-04 14:21:23 | A240 | 2
UVAC3 | 2020-11-04 14:21:23 | A240 | 3
UVAC3 | 2020-11-04 14:21:23 | A240 | 4
UVAC3 | 2020-11-04 15:19:39 | B410 | 1
UVAC3 | 2020-11-04 15:19:44 | B410 | 2
UVAC3 | 2020-11-04 15:19:58 | B410 | 3
UVAC3 | 2020-11-04 15:20:11 | B410 | 4
UVAC3 | 2020-11-04 15:22:16 | A240 | 1
UVAC3 | 2020-11-04 15:22:33 | A240 | 2
db<>fiddle here

Oracle - how to update a unique row based on MAX effective date which is part of the unique index

Oracle - Say you have a table that has a unique key on name, ssn and effective date. The effective date makes it unique. What is the best way to update a current indicator to show inactive for the rows with dates less than the max effective date? I can't really wrap my head around it since there are multiple rows with the same name and ssn combinations. I haven't been able to find this scenario on here for Oracle and I'm having developer's block. Thanks.
"All name/ssn having a max effective date earlier than this time yesterday:"
SELECT name, ssn
FROM t
GROUP BY name, ssn
HAVING MAX(eff_date) < SYSDATE - 1
Oracle supports multi column in, so
UPDATE t
SET current_indicator = 'inactive'
WHERE (name,ssn,eff_date) IN (
SELECT name, ssn, max(eff_date)
FROM t
GROUP BY name, ssn
HAVING MAX(eff_date) < SYSDATE - 1
)
Use a MERGE statement using an analytic function to identify the rows to update and then merge on the ROWID pseudo-column so that Oracle can efficiently identify the rows to update (without having to perform an expensive self-join by comparing the values):
MERGE INTO table_name dst
USING (
SELECT rid,
max_eff_date
FROM (
SELECT ROWID AS rid,
effective_date,
status,
MAX( effective_date ) OVER ( PARTITION BY name, ssn ) AS max_eff_date
FROM table_name
)
WHERE ( effective_date < max_eff_date AND status <> 'inactive' )
OR ( effective_date = max_eff_date AND status <> 'active' )
) src
ON ( dst.ROWID = src.rid )
WHEN MATCHED THEN
UPDATE
SET status = CASE
WHEN src.max_eff_date = dst.effective_date
THEN 'active'
ELSE 'inactive'
END;
So, for some sample data:
CREATE TABLE table_name ( name, ssn, effective_date, status ) AS
SELECT 'aaa', 1, DATE '2020-01-01', 'inactive' FROM DUAL UNION ALL
SELECT 'aaa', 1, DATE '2020-01-02', 'inactive' FROM DUAL UNION ALL
SELECT 'aaa', 1, DATE '2020-01-03', 'inactive' FROM DUAL UNION ALL
SELECT 'bbb', 2, DATE '2020-01-01', 'active' FROM DUAL UNION ALL
SELECT 'bbb', 2, DATE '2020-01-02', 'inactive' FROM DUAL UNION ALL
SELECT 'bbb', 3, DATE '2020-01-01', 'inactive' FROM DUAL UNION ALL
SELECT 'bbb', 3, DATE '2020-01-03', 'active' FROM DUAL;
The query only updates the 3 rows that need changing and:
SELECT *
FROM table_name;
Outputs:
NAME | SSN | EFFECTIVE_DATE | STATUS
:--- | --: | :------------- | :-------
aaa | 1 | 01-JAN-20 | inactive
aaa | 1 | 02-JAN-20 | inactive
aaa | 1 | 03-JAN-20 | active
bbb | 2 | 01-JAN-20 | inactive
bbb | 2 | 02-JAN-20 | active
bbb | 3 | 01-JAN-20 | inactive
bbb | 3 | 03-JAN-20 | active
db<>fiddle here

Oracle: Combine two group by queries (which use aggregate function count()) by union or so to get a consolidated result

I have two tables. TABLE_A and TABLE_B.
Both tables maintain columns to save CREATION_USER. But this column has different name in respective tables.
My motive is to get a count of records each user has created in both tables.
That is, combining result of these two queries with few conditions. The user name should not get repeated and for user names who have created records in both tables, the count should be their sum.
SELECT A.CREATION_USER_A AS "USER",
COUNT(*)
FROM TABLE_A A
GROUP BY A.CREATION_USER_A;
SELECT B.CREATION_USER_B AS "USER",
COUNT(*)
FROM TABLE_B B
GROUP BY B.CREATION_USER_B;
For e.g.,
USER_A has created 2 records in TABLE_A,
USER_B has created 3 records in TABLE_B and
USER_C has created 4 records in TABLE_A and 3 records in TABLE_B.
So the output should look like this:
| USER | COUNT |
| USER_A | 2 |
| USER_B | 3 |
| USER_C | 7 |
I have written a query which does this but it performs really bad.
SELECT A.CREATION_USER_A AS "USER",
(COUNT(A.CREATION_USER_A)+(SELECT COUNT(CREATION_USER_B) FROM TABLE_B WHERE CREATION_USER_B = A.CREATION_USER_A)) AS "COUNT"
FROM TABLE_A A
GROUP BY A.CREATION_USER_A
UNION
SELECT B.CREATION_USER_B,
COUNT(B.CREATION_USER_B)
FROM TABLE_B B
WHERE B.CREATION_USER_B NOT IN (SELECT CREATION_USER_A FROM TABLE_A)
GROUP BY B.CREATION_USER_B;
Please suggest a way to get this done.
You can simply build a set given by the union (keeping duplicates) of all the records in your tables, and then count the records grouping by creation user:
Bulding some sample data:
create table table_a(id, creation_user_a) as (
select 1, 'USER_A' from dual union all
select 1, 'USER_A' from dual union all
select 1, 'USER_C' from dual union all
select 1, 'USER_C' from dual union all
select 1, 'USER_C' from dual union all
select 1, 'USER_C' from dual
);
create table table_b(id, creation_user_b) as (
select 1, 'USER_B' from dual union all
select 1, 'USER_B' from dual union all
select 1, 'USER_B' from dual union all
select 1, 'USER_C' from dual union all
select 1, 'USER_C' from dual union all
select 1, 'USER_C' from dual
)
The query:
select count(1), creation_user
from ( /* the union of all the records from table_a and table_b */
select creation_user_a as creation_user from table_a
union all /* UNION ALL keeps duplicates */
select creation_user_B from table_b
)
group by creation_user
order by creation_user
The result:
2 USER_A
3 USER_B
7 USER_C
The explain plan:
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12 | 96 | 8 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 12 | 96 | 8 (25)| 00:00:01 |
| 2 | HASH GROUP BY | | 12 | 96 | 8 (25)| 00:00:01 |
| 3 | VIEW | | 12 | 96 | 6 (0)| 00:00:01 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| TABLE_A | 6 | 48 | 3 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL| TABLE_B | 6 | 48 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------
An alternative (but more complicated, and possibly slower - you'd need to test both to check) solution to Aleksej's answer is to use a full outer join to join both grouped by queries, like so:
WITH table_a AS (SELECT 'USER_A' creation_user_a, 10 val FROM dual UNION ALL
SELECT 'USER_A' creation_user_a, 20 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_a, 30 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_a, 40 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_a, 50 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_a, 60 val FROM dual),
table_b AS (SELECT 'USER_B' creation_user_b, 10 val FROM dual UNION ALL
SELECT 'USER_B' creation_user_b, 20 val FROM dual UNION ALL
SELECT 'USER_B' creation_user_b, 30 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_b, 40 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_b, 50 val FROM dual UNION ALL
SELECT 'USER_C' creation_user_b, 60 val FROM dual)
-- end of mimicking your tables with data in them. See the SQL below:
SELECT COALESCE(a.creation_user_a, b.creation_user_b) "USER",
nvl(a.cnt_a, 0) + nvl(b.cnt_b, 0) total_records
FROM (SELECT creation_user_a,
COUNT(*) cnt_a
FROM table_a
GROUP BY creation_user_a) a
FULL OUTER JOIN (SELECT creation_user_b,
COUNT(*) cnt_b
FROM table_b
GROUP BY creation_user_b) b ON a.creation_user_a = b.creation_user_b
ORDER BY "USER";
USER TOTAL_RECORDS
------ -------------
USER_A 2
USER_B 3
USER_C 7
Thank you for helping me guys. I have found a simpler and more efficient solution. It works.
SELECT CREATION_USER, SUM(TOTAL_COUNT) TOTAL_COUNT FROM
(SELECT /*+ PARALLEL */ A.CREATION_USER_A CREATION_USER,
COUNT(A.CREATION_USER_A) TOTAL_COUNT
FROM TABLE_A A
GROUP BY A.CREATION_USER_A
UNION
SELECT /*+ PARALLEL */ B.CREATION_USER_B CREATION_USER,
COUNT(B.CREATION_USER_B) TOTAL_COUNT
FROM TABLE_B B
GROUP BY B.CREATION_USER_B)
GROUP BY CREATION_USER;

Find highest and lowest selling item in a table

I have two tables as follows--
ORDERS
create table orders (
ono number(5) not null primary key,
cno number(5) references customers,
eno number(4) references employees,
received date,
shipped date);
ODETAILS
create table odetails (
ono number(5) not null references orders,
pno number(5) not null references parts,
qty integer check(qty > 0),
primary key (ono,pno));
ODETAILS Table
Now I'm trying to figure out the highest and lowest selling product. Logically PNO 10601 which has the highest QTY of 4 is the highest selling product. the following query returns the highest selling product.
SELECT PNO FROM
(SELECT od.PNO, SUM(od.QTY) AS TOTAL_QTY
FROM ODETAILS od
GROUP BY od.PNO
ORDER BY SUM(od.QTY) DESC)
WHERE ROWNUM =1
--Thanks to Bob Jarvis
How do I add a DATE WHERE clause to the SQL above so that I can find out the highest selling product for a given month(lets say DECEMBER) ? The DATE that I'm referring to is from ORDERS table and RECEIVED attribute. I guess I need to join the tables first as well
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table orders (
ono number(5) not null primary key,
cno number(5),
eno number(4),
received date,
shipped date
);
INSERT INTO orders
SELECT 1020, 1, 1, DATE '2015-12-21', NULL FROM DUAL UNION ALL
SELECT 1021, 1, 1, DATE '2015-12-20', DATE '2015-12-20' FROM DUAL UNION ALL
SELECT 1022, 1, 1, DATE '2015-12-18', DATE '2015-12-20' FROM DUAL UNION ALL
SELECT 1023, 1, 1, DATE '2015-12-21', NULL FROM DUAL UNION ALL
SELECT 1024, 1, 1, DATE '2015-12-20', DATE '2015-12-20' FROM DUAL;
create table odetails (
ono number(5) not null references orders(ono),
pno number(5) not null,
qty integer check(qty > 0),
primary key (ono,pno)
);
INSERT INTO odetails
SELECT 1020, 10506, 1 FROM DUAL UNION ALL
SELECT 1020, 10507, 1 FROM DUAL UNION ALL
SELECT 1020, 10508, 2 FROM DUAL UNION ALL
SELECT 1020, 10509, 3 FROM DUAL UNION ALL
SELECT 1021, 10601, 4 FROM DUAL UNION ALL
SELECT 1022, 10601, 1 FROM DUAL UNION ALL
SELECT 1022, 10701, 1 FROM DUAL UNION ALL
SELECT 1023, 10800, 1 FROM DUAL UNION ALL
SELECT 1024, 10900, 1 FROM DUAL;
Query 1 - The onoand pnos for the pno which has sold the maximum total quantity in December 2015:
SELECT ono,
pno,
TOTAL_QTY
FROM (
SELECT q.*,
RANK() OVER ( ORDER BY TOTAL_QTY DESC ) AS rnk
FROM (
SELECT od.ono,
od.PNO,
SUM( od.QTY ) OVER ( PARTITION BY od.PNO ) AS TOTAL_QTY
FROM ODETAILS od
INNER JOIN
orders o
ON ( o.ono = od.ono )
WHERE TRUNC( o.received, 'MM' ) = DATE '2015-12-01'
-- WHERE EXTRACT( MONTH FROM o.received ) = 12
) q
)
WHERE rnk = 1
Change the WHERE clause to get the results for any December rather than just December 2015.
Results:
| ONO | PNO | TOTAL_QTY |
|------|-------|-----------|
| 1021 | 10601 | 5 |
| 1022 | 10601 | 5 |
Query 2 - The ono and pnos for the items which have sold the maximum quantity in a single order in December 2015:
SELECT ono,
pno,
qty
FROM (
SELECT od.*,
RANK() OVER ( ORDER BY od.qty DESC ) AS qty_rank
FROM ODETAILS od
INNER JOIN
orders o
ON ( o.ono = od.ono )
WHERE TRUNC( o.received, 'MM' ) = DATE '2015-12-01'
-- WHERE EXTRACT( MONTH FROM o.received ) = 12
)
WHERE qty_rank = 1
Change the WHERE clause to get the results for any December rather than just December 2015.
Results:
| ONO | PNO | QTY |
|------|-------|-----|
| 1021 | 10601 | 4 |
... where received between to_date('12/01/2015','MM/DD/YYYY') and to_date('12/31/2015','MM/DD/YYYY')
I believe I have solved it!
SELECT PNO
FROM (SELECT OD.PNO, SUM(OD.QTY) AS TOTAL_QTY
FROM ODETAILS OD INNER JOIN ORDERS ON OD.ONO = ORDERS.ONO
WHERE EXTRACT(MONTH FROM ORDERS.RECEIVED) = &MONTH_NUMBER
GROUP BY OD.PNO
ORDER BY SUM(OD.QTY) DESC)
WHERE ROWNUM =1;
You can add some to_char calls to your query on the date columns to parse out year and month, or just month if you want all years divided by month (month and year seems more useful), then add that to your where clause. See my self-contained example:
with odetails as
(
select 1 as ono, 1 as pno, 4 as qty from dual
union all
select 1 as ono, 2 as pno, 1 as qty from dual
union all
select 1 as ono, 3 as pno, 2 as qty from dual
union all
select 1 as ono, 4 as pno, 1 as qty from dual
union all
select 2 as ono, 2 as pno, 1 as qty from dual
union all
select 2 as ono, 3 as pno, 2 as qty from dual
),
orders as
(
select 1 as ono, 1 as cno, 1 as eno, to_date('2015-10-12', 'YYYY-MM-DD') as received, to_date('2015-10-15', 'YYYY-MM-DD') as shipped from dual
union all
select 2 as ono, 1 as cno, 1 as eno, to_date('2015-11-12', 'YYYY-MM-DD') as received, to_date('2015-11-15', 'YYYY-MM-DD') as shipped from dual
)
select pno
from
(
select od.pno, Sum(od.qty) as total_qty, to_char(received, 'YYYY-MM') as year_month
from odetails od
join orders o
on o.ono = od.ono
group by od.pno, to_char(received, 'YYYY-MM')
order by Sum(od.qty) desc
)
where rownum = 1
and year_month = '2015-11'
;
This gives you PNO of 3, since it has the highest quantity in november of 2015.

How to use subqueries in Oracle?

I have the following table:
table1
-------------------------
date | ID | name
-------------------------
13-jul-15 | 1 | abc
13-jul-15 | 2 | abc
14-jul-15 | 1 | def
13-jul-15 | 3 | abc
15-jul-15 | 3 | def
...
What I want to do is match the ID and represent as below:
date1 | name | date2 | name | ID
------------------------------------------
13-jul-15 | abc | 14-jul-15 | def | 1
13-jul-15 | abc | | | 2
13-jul-15 | abc | 15-jul-15 | def | 3
...
I have used the following code, but not getting the result.
CREATE PROCEDURE get_details ( oresults1 OUT SYS_REFCURSOR ) AS
BEGIN
SELECT *
FROM ((SELECT date, ID FROM table1 WHERE name= "abc") T1
UNION ALL
(SELECT date, ID FROM table1 WHERE name= "def") T2
)
WHERE T1.ID= T2.ID
ORDER BY ID;
END;
What have I done wrong?
Here are a couple of alternatives:
with table1 as (select to_date('13/07/2015', 'dd/mm/yyyy') dt, 1 id, 'abc' name from dual union all
select to_date('13/07/2015', 'dd/mm/yyyy') dt, 2 id, 'abc' name from dual union all
select to_date('14/07/2015', 'dd/mm/yyyy') dt, 1 id, 'def' name from dual union all
select to_date('13/07/2015', 'dd/mm/yyyy') dt, 3 id, 'abc' name from dual union all
select to_date('15/07/2015', 'dd/mm/yyyy') dt, 3 id, 'def' name from dual)
-- end of mimicking your table1. See below for the query
select t1.dt date1,
t1.name name1,
t2.dt date2,
t2.name name2,
t1.id
from table1 t1
left outer join table1 t2 on (t1.id = t2.id and t1.name = 'abc' and t2.name = 'def')
where t1.name = 'abc'
order by t1.id;
DATE1 NAME1 DATE2 NAME2 ID
---------- ----- ---------- ----- ----------
13/07/2015 abc 14/07/2015 def 1
13/07/2015 abc 2
13/07/2015 abc 15/07/2015 def 3
with table1 as (select to_date('13/07/2015', 'dd/mm/yyyy') dt, 1 id, 'abc' name from dual union all
select to_date('13/07/2015', 'dd/mm/yyyy') dt, 2 id, 'abc' name from dual union all
select to_date('14/07/2015', 'dd/mm/yyyy') dt, 1 id, 'def' name from dual union all
select to_date('13/07/2015', 'dd/mm/yyyy') dt, 3 id, 'abc' name from dual union all
select to_date('15/07/2015', 'dd/mm/yyyy') dt, 3 id, 'def' name from dual)
-- end of mimicking your table1. See below for the query
select t1.dt date1,
t1.name name1,
t2.dt date2,
t2.name name2,
t1.id
from (select id, dt, name from table1 where name = 'abc') t1
left outer join (select id, dt, name from table1 where name = 'def') t2 on (t1.id = t2.id)
where t1.name = 'abc'
order by t1.id;
DATE1 NAME1 DATE2 NAME2 ID
---------- ----- ---------- ----- ----------
13/07/2015 abc 14/07/2015 def 1
13/07/2015 abc 2
13/07/2015 abc 15/07/2015 def 3
You can also use pivot function available in Oracle
WITH table_(date#, id#, name#) AS
(SELECT to_date('13-jul-15', 'dd-mon-yy'), 1, 'abc' FROM dual UNION all
SELECT to_date('13-jul-15', 'dd-mon-yy'), 2, 'abc' FROM dual UNION all
SELECT to_date('14-jul-15', 'dd-mon-yy'), 1, 'def' FROM dual UNION all
SELECT to_date('13-jul-15', 'dd-mon-yy'), 3, 'abc' FROM dual UNION all
SELECT to_date('15-jul-15', 'dd-mon-yy'), 3, 'def' FROM dual)
--------
-- End of data preparation
--------
SELECT *
FROM table_
PIVOT (MIN(date#) AS date#, MIN(name#) AS NAME# FOR name# IN ('abc' AS ABC, 'def' AS DEF));
Output
| ID# | ABC_DATE# | ABC_NAME# | DEF_DATE# | DEF_NAME# |
|-----|------------------------|-----------|------------------------|-----------|
| 1 | July, 13 2015 00:00:00 | abc | July, 14 2015 00:00:00 | def |
| 2 | July, 13 2015 00:00:00 | abc | | |
| 3 | July, 13 2015 00:00:00 | abc | July, 15 2015 00:00:00 | def |

Resources