PIVOT table in Oracle - oracle

Can you help me to pivot the details in my Oracle table PAY_DETAILS
PAY_NO NOT NULL NUMBER
EMP_NO NOT NULL VARCHAR2(10)
EMP_ERN_DDCT_NO NOT NULL VARCHAR2(21)
ERN_DDCT_CATNO NOT NULL VARCHAR2(10)
ERN_DDCT_CATNAME NOT NULL VARCHAR2(1000)
PAY_MONTH NOT NULL DATE
AMOUNT NOT NULL NUMBER(10,2)
EARN_DEDUCT NOT NULL VARCHAR2(2)
select EMP_NO,EMP_ERN_DDCT_NO,AMOUNT,EARN_DEDUCT, ERN_DDCT_CATNO from pay_details
EMP_NO EMP_ERN_DDCT_NO AMOUNT EA ERN_DDCT_C
---------- --------------------- ---------- -- ----------
219 10 175 A 001
219 1 5000 A 002
794 7 50000 A 001
769 6 35000 A 001
465 4 5000 A 002
289 2 5000 A 002
435 3 5000 A 002
816 38 5 D 201
737 30 5 D 201
Is it possible to make this output into a cross tab?

So, lets assume you want to pivot your salary data for each month. You can use the following query.
SELECT * FROM
(
SELECT emp_no,
emp_ern_ddct_no,
pay_month,
amount
FROM pay_details
)
PIVOT
(
SUM(amount)
FOR pay_month IN ('01/01/2016', '02/01/2016', '03/01/2016','04/01/2016','05/01/2016','06/01/2016','07/01/2016','08/01/2016','09/01/2016','10/01/2016','11/01/2016','12/01/2016')
)
ORDER BY emp_no;
This is just an example, you can PIVOT your data based on different columns. For more details refer to the following link.
http://www.techonthenet.com/oracle/pivot.php
http://www.oracle-developer.net/display.php?id=506
Since you are on Oracle 10g PIVOT wont work. Try using something similar to the below query.
SELECT emp_no,
SUM(CASE WHEN pay_month ='01/01/2016' THEN AMOUNT ELSE 0 END) jan_pay,
SUM(CASE WHEN pay_month ='02/01/2016' THEN AMOUNT ELSE 0 END) feb_pay,
SUM(CASE WHEN pay_month ='03/01/2016' THEN AMOUNT ELSE 0 END) march_pay
.........
FROM pay_details
GROUP BY emp_no;

Related

Query to find before and after values of a given value

If we have table Employees
EMP_ID ENAME SALARY DEPT_ID
1 abc 1000 10
2 bca 1050 10
3 dsa 2000 20
4 zxc 3000 30
5 bnm 5000 30
6 rty 5050 30
I want to get the rank of the salary with before 2 values and after 2 values including the given rank
Like if I give rank 4 it should give ranks 2,3,4,5,6 details.
output should be
5 bnm 5000 30
4 zxc 3000 30
3 dsa 2000 20
3 dsa 2000 20
2 bca 1050 10
1 abc 1000 10
I have a query
WITH dept_count AS (
SELECT
e.*,
dense_rank() over( ORDER BY salary DESC) AS rk
FROM employees e
)
SELECT
*
FROM dept_count dc
WHERE dc.rk BETWEEN (
SELECT
c.rk-2
FROM dept_count c
WHERE c.rk =4
)
AND (
SELECT
c.rk + 2
FROM dept_count c
WHERE c.rk = 4
)
but I need a query which can be simplified.
Could someone help me with this query?
You just need to use ROW_NUMBER() along with a substitution parameter :
WITH dept_count AS (
SELECT
e.*,
ROW_NUMBER() OVER( ORDER BY salary DESC) AS rk
FROM employees e
)
SELECT *
FROM dept_count
WHERE rk BETWEEN &prm - 2 AND &prm + 2

Split the data across multiple columns in student Table using pl/sql or sql

Please help me. Currently I’m working on pl/sql procedure. In sport table has ID and for same id has multiple codes. I need to split these multiple codes and insert them to student tables as code1,codes2,code3.
Source table
ID CODE
----------
222 4wta
----------
223 5qer
----------
222 5qer
-----------
224 3der
---------
Desired table
ID CODE1 CODE2 CODE3
-------------------------
222 4wta 5qer NULL
-------------------------
223 5qer NULL NULL
------------------------
224 3der NULL NULL
------------------------
With a little help of analytic function (to decide which CODEn to fetch) and aggregation, you'd have this (presuming that there are max 3 codes per each ID).
Sample data:
SQL> select * From src;
ID CODE
---------- ----
222 4wta
223 5qer
222 5qer
224 3der
Inserting:
SQL> insert into trg (id, code1, code2, code3)
2 with temp as
3 (select id, code,
4 row_number() over (partition by id order by code) rn
5 from src
6 )
7 select id,
8 max(case when rn = 1 then code end) code1,
9 max(case when rn = 2 then code end) code2,
10 max(case when rn = 3 then code end) code3
11 from temp
12 group by id;
3 rows created.
Result:
SQL> select * From trg;
ID CODE1 CODE2 CODE3
---------- ----- ----- -----
222 4wta 5qer
223 5qer
224 3der
SQL>

CTAS query in Oracle for Index Organized tables

Is it possible to write a CTAS (create table as select ...) query in Oracle to create Index Organized tables ?
Have looked on the net and tried a few versions of the CTAS, but unable to find a working sample/quide query.
Short answer: yes.
Example, based on the JOB_HISTORY table:
table JOB_HISTORY
SQL> select * from hr.job_history ;
EMPLOYEE_ID START_DATE END_DATE JOB_ID DEPARTMENT_ID
102 13-JAN-93 24-JUL-98 IT_PROG 60
101 21-SEP-89 27-OCT-93 AC_ACCOUNT 110
101 28-OCT-93 15-MAR-97 AC_MGR 110
201 17-FEB-96 19-DEC-99 MK_REP 20
114 24-MAR-98 31-DEC-99 ST_CLERK 50
122 01-JAN-99 31-DEC-99 ST_CLERK 50
200 17-SEP-87 17-JUN-93 AD_ASST 90
176 24-MAR-98 31-DEC-98 SA_REP 80
176 01-JAN-99 31-DEC-99 SA_MAN 80
200 01-JUL-94 31-DEC-98 AC_ACCOUNT 90
Query (for generating sample data)
-- every employee must do every job (only for a day at a time ...)
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J ;
--
EMPLOYEE_ID JOB_ID DT
101 AC_ACCOUNT 10-FEB-20
101 AC_MGR 09-FEB-20
101 AD_ASST 08-FEB-20
101 IT_PROG 07-FEB-20
101 MK_REP 06-FEB-20
101 SA_MAN 05-FEB-20
101 SA_REP 04-FEB-20
101 ST_CLERK 03-FEB-20
102 AC_ACCOUNT 02-FEB-20
102 AC_MGR 01-FEB-20
...
200 ST_CLERK 25-DEC-19
201 AC_ACCOUNT 24-DEC-19
201 AC_MGR 23-DEC-19
201 AD_ASST 22-DEC-19
201 IT_PROG 21-DEC-19
201 MK_REP 20-DEC-19
201 SA_MAN 19-DEC-19
201 SA_REP 18-DEC-19
201 ST_CLERK 17-DEC-19
--56 rows selected.
CTAS
-- heap organized table
create table heap_empjobs (
employee_id, job_id, dt
, constraint heap_empjobs_pk primary key( employee_id, job_id, dt )
)
as
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J
;
-- Table created.
-- index organized table
create table iot_empjobs (
employee_id, job_id, dt
, constraint iot_pk primary key( employee_id, job_id, dt ) -- also works without naming the constraint
)
organization index
as
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J
;
-- Table created.
Checks
select count(*) from heap_empjobs ;
COUNT(*)
56
select count(*) from iot_empjobs ;
COUNT(*)
56
select table_name, nvl( to_char( num_rows ), 'no rows!' ) rowcount
from user_tables
where table_name in ('HEAP_EMPJOBS', 'IOT_EMPJOBS') ;
TABLE_NAME ROWCOUNT
HEAP_EMPJOBS 56
IOT_EMPJOBS no rows!
Tested with Oracle 18c and 11g (see dbfiddle).

Using case update in cursor

I have below 2 tables. One is dept a
DEPT_NO DEPT_NAME DEPT_VALUE
---------- ------------------------------ ----------
10 Chemistry 100
40 Physics 600
20 Mathematics 200
30 Biology 300
50 Cosmos 550
other one is updated_dept b
DEPT DEPT_NAME DEPT_UPDATED_VALUE
---------- ------------------------------ ------------------
10 Chemistry
20 Mathematics
30 Biology
90 Astrology
40 Numerology
50 Cosmos
With the help of the cursor, I want to fetch a.dept_value from 1st table dept a and on basis of (a.dept_no=b.dept and a.dept_name=b.dept_name) update column dept_updated_value in 2nd table updated_dept b using case.
If combination of dept_no and dept_name is not found, I want to update dept_updated_value to 0
I have written below code but it's not giving correct result. Please help
declare
v_dept dept.dept_no%type;
v_dept_name dept.dept_name%type;
v_dept_value dept.dept_value%type;
cursor c_dept_update
is
select dept_no, dept_name, dept_value from dept;
begin
open c_dept_update;
loop
fetch c_dept_update into v_dept,v_dept_name, v_dept_value;
exit when c_dept_update%notfound;
update dept_updated
set dept_updated_value=
case
when dept=v_dept and dept_name=v_dept_name
then v_dept_value
else 0
end;
commit;
end loop;
close c1;
end;
result is like this
DEPT DEPT_NAME DEPT_UPDATED_VALUE
---------- ---------------------------------- ------------------
10 Chemistry 0
20 Mathematics 0
30 Biology 0
90 Astrology 0
40 Numerology 0
50 Cosmos 550
To paraphrase Steven Feuerstein, don't do in a loop what you can do in SQL. So I'd join the tables in an updatable inline view. I haven't tested this so it may need tweaking:
UPDATE ( SELECT b.dept, b.dept_name, b.dept_updated_value, a.dept AS dept2
FROM updated_dept b
LEFT JOIN dept a ON a.dept = b.dept AND a.dept_name = b.dept_name )
SET dept_updated_value = NVL( dept2, 0 )

Convert rows to columns like pivot

I have a data like:
formid formownerid approverid
1 100 102
1 100 103
1 100 104
2 200 107
2 200 103
2 200 109
2 200 105
3 400 201
3 400 210
I want to convert it to:
formid formownerid approverid approverid approverid approverid
1 100 102 103 104 NULL
2 200 107 103 109 105
3 400 201 202 NULL NULL
Wherever I looked at I saw pivot/unpivot but it looks unrelated since we don't need aggregation.
The aggregate is a necessary part of the pivot, but it's simple to apply here; you don't want sum but a max aggregate works fine:
select *
from (
select t.*,
row_number() over (partition by formid, formownerid
order by approverid) as rn
from t42 t
)
pivot (max(approverid) as approverid for (rn) in (1, 2, 3, 4));
FORMID FORMOWNERID 1_APPROVERID 2_APPROVERID 3_APPROVERID 4_APPROVERID
---------- ----------- ------------ ------------ ------------ ------------
1 100 102 103 104
2 200 103 105 107 109
3 400 201 210
Or you can specify the column name prefix explicitly to make them valid identifiers:
pivot (max(approverid) as approverid
for (rn) in (1 as a, 2 as b, 3 as c, 4 as d));
The inner query is adding a pseudocolumn rn to the table results to give you a fixed value to pivot against, since the actual approver IDs aren't going to be known in advance.
The manual approach might make this a bit clearer:
select formid, formownerid,
max(case when rn = 1 then approverid end) as approverid_1,
max(case when rn = 2 then approverid end) as approverid_2,
max(case when rn = 3 then approverid end) as approverid_3,
max(case when rn = 4 then approverid end) as approverid_4
from (
select t.*,
row_number() over (partition by formid, formownerid
order by approverid) as rn
from t42 t
)
group by formid, formownerid
order by formid, formownerid;
FORMID FORMOWNERID APPROVERID_1 APPROVERID_2 APPROVERID_3 APPROVERID_4
---------- ----------- ------------ ------------ ------------ ------------
1 100 102 103 104
2 200 103 105 107 109
3 400 201 210
The inner query is the same. The case statement produces each column as above, but without the max and grouping you get multiple rows with lots of extra blanks:
select formid, formownerid,
case when rn = 1 then approverid end as approverid_1,
case when rn = 2 then approverid end as approverid_2,
case when rn = 3 then approverid end as approverid_3,
case when rn = 4 then approverid end as approverid_4
from (
select t.*,
row_number() over (partition by formid, formownerid
order by approverid) as rn
from t42 t
);
FORMID FORMOWNERID APPROVERID_1 APPROVERID_2 APPROVERID_3 APPROVERID_4
---------- ----------- ------------ ------------ ------------ ------------
1 100 102
1 100 103
1 100 104
2 200 103
2 200 105
2 200 107
2 200 109
3 400 201
3 400 210
Notice that there's only a value in (at most) one column for each formid/formownerid combination, but that they appear in different rows. The max suppresses those multiple rows; and the pivot version does something similar under the hood.
SQL Fiddle showing the manual approach with the intermediate step, and the pivot version.
One possible Approch:
SELECT FROMID, FROMOWNERID, APPROVERID, NULL APPROVERID, NULL APPROVERID, NULL APPROVERID
FROM yourtable
WHERE FROMID = 100
AND APPROVERID = 102
UNION ALL
SELECT FROMID, FROMOWNERID, NULL APPROVERID, APPROVERID APPROVERID, NULL APPROVERID, NULL APPROVERID
FROM yourtable
WHERE FROMID = 100
AND APPROVERID = 103
UNION ALL
SELECT FROMID, FROMOWNERID, NULL APPROVERID, NULL APPROVERID, APPROVERID APPROVERID, NULL APPROVERID
FROM yourtable
WHERE FROMID = 100
AND APPROVERID = 104
----
-------
And So On

Resources