Convert rows to columns like pivot - oracle

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

Related

Retrieving profiles from a table based on specific logic

I have a requirement to extract profiles from a table for the below criteria:
There are 5 IDs in total. 100, 200, 300, 400 and 500
The profiles to have atleast one ID of each value 100, 300 and 400. IDs with value 200 and 500 should not be present even once.
Profile can have multiple number of IDs of value 300 and 400 but will have only one ID of value 100.
The profiles will have IDs 300 and 400 equally. i.e For every ID will the value 300, there will be an ID of value 400.
Eg:
TABLE A:
-------------------------
PROFILE_ID ID
-------------------------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
-------------------------
The result should fetch PROFILE_IDs 12345 and 23456 and not 34567.
I am pretty stuck and blank in getting a clear idea on how to frame a query for this. Please help.
For sample data you posted:
SQL> select * From test;
PROFILE_ID ID
---------- ----------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
10 rows selected.
one option is to do it rule-by-rule, each of CTEs retrieving data which satisfy certain rule. The final result is intersection of these PROFILE_IDs.
SQL> with
2 rule2 as -- don't allow IDs 200 nor 500
3 (select profile_id
4 from test
5 where profile_id not in (select profile_id
6 from test
7 where id in (200, 500)
8 )
9 ),
10 rule3 as -- there can be only one ID = 100 for each PROFILE_ID
11 (select profile_id
12 from (select profile_id,
13 sum(case when id = 100 then 1 else 0 end) cnt_100
14 from test
15 group by profile_id
16 )
17 where cnt_100 = 1
18 ),
19 rule4 as -- number of IDs 300 and 400 has to be equal and greater than 0
20 (select profile_id
21 from (select profile_id,
22 sum(case when id = 300 then 1 else 0 end) cnt_300,
23 sum(case when id = 400 then 1 else 0 end) cnt_400
24 from test
25 group by profile_id
26 )
27 where cnt_300 = cnt_400
28 and cnt_300 > 0
29 )
30 select profile_id from rule2
31 intersect
32 select profile_id from rule3
33 intersect
34 select profile_id from rule4;
PROFILE_ID
----------
12345
23456
SQL>
Personally, since all your rules rely on counting the different ids, I'd do it more like this. Some of this is redundant, but it makes explicit which part comes from which of your criteria.
with rules as (
select profile_id,
sum(case when id = 100 then 1 else 0 end) as cnt_100,
sum(case when id = 200 then 1 else 0 end) as cnt_200,
sum(case when id = 300 then 1 else 0 end) as cnt_300,
sum(case when id = 400 then 1 else 0 end) as cnt_400,
sum(case when id = 500 then 1 else 0 end) as cnt_500
from table1
group by profile_id
)
select profile_id
from rules
-- rule 1
where cnt_100 > 0
and cnt_300 > 0
and cnt_400 > 0
-- rule 2
and cnt_200 = 0
and cnt_500 = 0
-- rule 3
and cnt_100 = 1
-- rule 4
and cnt_300 = cnt_400
SQL Fiddle

Oracle- count the number rows based on a condition

I want to create a 'ticket' which counts the number of passes for each ID. When we have a gold pass on any of the ID, this means the pass is applied to all those in booking. So for this example, we want to count 5. For the other pass_codes, we want to simply count the number of passes and exclude those that are nulls. I have an expected output below.
Say I have this data:
Passes
ID | GuestID | Pass_code
----------------------------
100 | 001 | Bronze
100 | 002 | Bronze
101 | 103 | Gold
101 | 104 | NULL
101 | 105 | NULL
101 | 106 | NULL
101 | 107 | NULL
102 | 208 | Silver
103 | 209 | Steel
103 | 210 | Steel
103 | 211 | NULL
Passengers
ID | Passengers
-----------------
100 | 2
101 | 5
102 | 1
103 | 3
I want to count then create a ticket in the output of:
ID 100 | 2 pass (bronze)
ID 101 | 5 pass (because it is gold, we count all passengers)
ID 102 | 1 pass (silver)
ID 103 | 2 pass (steel) (2 passes rather than than 3 as we just want to count only the passes for steel, bronze silver)
I want to do something like this, but as a combined query.
DECLARE #ID = 101; -- i will want to pass in IDs
-- for gold, we want to count all passengers when the gold pass is on
SELECT pp.Passengers
FROM passes
JOIN Passengers pp ON p.ID = pp.ID
WHERE p.pass_code IN'%gold%'
AND PP.id = #id
-- for bronze, silver and steel
SELECT
count(p.ID)
FROM Passes
WHERE p.ID = #id
AND P.pass_code IN ('Bronze', 'silver', 'steel') -- Dont want to check based on NUlls as this may chnage to something else.
)
Any help or advice would be much appreciated.
Does this work for you?
with Passes as (
select 100 as id, 001 as guestid, 'Bronze' as passcode from dual union all
select 100 as id, 002 as guestid, 'Bronze' as passcode from dual union all
select 101 as id, 103 as guestid,'Gold' as passcode from dual union all
select 101 as id, 104 as guestid, NULL as passcode from dual union all
select 101 as id, 105 as guestid, NULL as passcode from dual union all
select 101 as id, 106 as guestid, NULL as passcode from dual union all
select 101 as id, 107 as guestid, NULL as passcode from dual union all
select 102 as id, 208 as guestid, 'Silver' as passcode from dual union all
select 103 as id, 209 as guestid, 'Steel' as passcode from dual union all
select 103 as id, 210 as guestid, 'Steel' as passcode from dual union all
select 103 as id, 211 as guestid, NULL as passcode from dual
)
SELECT
id,passcode,count(ID)
FROM Passes
where passcode is not null and passcode<>'Gold'
group by id,passcode
union all
SELECT
id,'Gold',count(ID)
FROM Passes
where id in
(
select id from Passes where passcode='Gold'
)
group by id
order by id
result:
100 Bronze 2
101 Gold 5
102 Silver 1
103 Steel 2
If I understand your question right the query should be like the following
** The table
create table test (ID number, GuestId number, Pass_code varchar2(10));
insert into test values(100,001,'Bronze');
insert into test values(100,002,'Bronze');
insert into test values(101,103,'Gold');
insert into test values(101,104,NULL);
insert into test values(101,105,NULL);
insert into test values(101,106,NULL);
insert into test values(101,107,NULL);
insert into test values(102,208,'Silver');
insert into test values(103,209,'Steel');
insert into test values(103,210,'Steel');
insert into test values(103,211,NULL);
commit;
SQL> select * from test order by 1,2;
ID GUESTID PASS_CODE
---------- ---------- ------------------------------
100 1 Bronze
100 2 Bronze
101 103 Gold
101 104
101 105
101 106
101 107
102 208 Silver
103 209 Steel
103 210 Steel
103 211
11 rows selected.
** The query
WITH PASSES AS (
SELECT T1.ID,T1.PASS_CODE, COUNT(T2.ID) QUANTITY FROM TEST T1, TEST T2
WHERE T1.PASS_CODE='Gold' AND T1.ID=T2.ID
GROUP BY T1.ID,T1.PASS_CODE
UNION ALL
SELECT ID,PASS_CODE, COUNT(*) QUANTITY FROM TEST
WHERE PASS_CODE IS NOT NULL AND
PASS_CODE != 'Gold'
GROUP BY ID,PASS_CODE)
SELECT ID, QUANTITY || ' (' || PASS_CODE || ')' RESULT FROM PASSES
ORDER BY ID;
** Result
ID RESULT
---------- --------------------
100 2 (Bronze)
101 5 (Gold)
102 1 (Silver)
103 2 (Steel)

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>

PIVOT table in 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;

sql query to get the column data in one row

I have below data in a table called data_tab
sn code
2 101
2
2 202
5 103
5
5
How can i query to see result in one row, like
sn code1 code2 code3
2 101 202
5 103
Hi This gives the intented output ... take a look here
select sn,
max(decode(rn,1,code)) as CODE_1
,max(decode(rn,2,code)) as CODE_2
,max(decode(rn,3,code)) as CODE_3
from
(
select sn,
code,
row_number() over (partition by sn order by null ) rn
from test
)
group by sn

Resources