Oracle 9.2 pivot distinct value - oracle

The pivot function is available from Oracle 11 and i will need similar result using Oracle 9.2.
The main argument is that i need to pivot some values with a distinct result in a table like this:
id col3
1 a
1 b
--
2 a
2 a
2 b
--
3 a
3 b
3 c
My result sould be something like this
id a b c
1 1 1 0
2 1 1 0
3 1 1 1
To create a "manual" pivot i'm using a case/when but I am not able to understand how to get distinct value.
Right now the query is this:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select t.id,
count(case when t.col1 = 'a' then 1 end) a,
count(case when t.col1 = 'b' then 1 end) b,
count(case when t.col1 = 'c' then 1 end) c
This produce correct value but obviously it just "count" the total a/b/c value and not the distinct.
thanks for the support

If I correctly understand your need, you can try something like the following; it aggregates by id and counts the distinct values of col3:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select id,
count(distinct decode (col1, 'a', id, null)) a,
count(distinct decode (col1, 'b', id, null)) b,
count(distinct decode (col1, 'c', id, null)) c
from t
group by id
Of course the query depends on the number of different values of col3, but this is the same problem than pivot.

Related

Select statement with a dynamic group by clause

How possible to group records conditionally? For example I have records with FLOW_NUMBER column, values 1, 2, 3. When FLOW_NUMBER value is 1, I want to group this records, but when FLOW_NUMBER is 2 or 3, I don't want to group.
SELECT
LISTAGG(DISTINCT PRODORD, ',') PRODORD,
SUM(QTY) AS QTY
FROM (
SELECT
1 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
1 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
1 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
)
GROUP BY FLOW_NUMBER -- bad group by clause
There is example photo of group by which I need.
SELECT SUBSTR(flow,1,INSTR(flow,'/')-1) AS flow, LISTAGG(prodorder,',') WITHIN GROUP(ORDER BY prodorder) AS prodorders, SUM(qty) AS qty
FROM (
SELECT CASE WHEN flow=1 THEN '1/' ELSE to_CHAR(flow)||'/'||ROWNUM END AS flow,
prodorder, qty FROM DATA
)
GROUP BY flow;
I don't think there is anyway to achieve it except using 2 query combined with UNION ALL clause -
SELECT FLOW_NUMBER, LISTAGG(DISTINCT PRODORD, ',') PRODORD,
SUM(QTY) AS QTY
FROM temp
WHERE FLOW_NUMBER = 1
GROUP BY FLOW_NUMBER
UNION ALL
SELECT FLOW_NUMBER, PRODORD,
QTY
FROM temp
WHERE FLOW_NUMBER <> 1;
Demo.
You can try this:
select
substr(fn,1,decode(instr(fn,','),0,1000,instr(fn,','))-1) flow_number,
prodord, qty
from (SELECT
case when FLOW_NUMBER=1 then to_char(flow_number) else to_char(flow_number)||','||rownum end fn,
LISTAGG(DISTINCT PRODORD, ',') PRODORD,
SUM(QTY) AS QTY
FROM (
SELECT
1 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
1 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
1 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
2 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'A' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'B' AS PRODORD,
1 AS QTY
FROM DUAL
UNION ALL
SELECT
3 AS FLOW_NUMBER,
'C' AS PRODORD,
1 AS QTY
FROM DUAL
)
GROUP BY case when FLOW_NUMBER=1 then to_char(flow_number) else to_char(flow_number)||','||rownum end);
I used rownum here, but you can probably replace it by other things (e.g. rowid, if the data is from a table, just be sure to have a unique value)

Duplicated rows numbering

I need to number the rows so that the row number with the same ID is the same. For example:
Oracle database. Any ideas?
Use the DENSE_RANK analytic function:
SELECT DENSE_RANK() OVER (ORDER BY id) AS row_number,
id
FROM your_table
Which, for the sample data:
CREATE TABLE your_table ( id ) AS
SELECT 86325 FROM DUAL UNION ALL
SELECT 86325 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86352 FROM DUAL UNION ALL
SELECT 86353 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL;
Outputs:
ROW_NUMBER
ID
1
86325
1
86325
2
86326
2
86326
3
86352
4
86353
5
86354
5
86354
db<>fiddle here

Group Query for oracle

I have a scenario where I need to fetch all the records within an ID for the one source along with other source. Given below is my input set of records.
ID SOURCE CURR_FLAG TYPE
1 IBM Y P
1 IBM Y OF
1 IBM Y P
2 IBM Y P
2 TCS Y P
3 IBM NULL P
3 CTS NULL P
3 TCS NULL P
4 IBM NULL OF
4 CTS NULL OF
4 TCS Y ON
5 CTS NULL OF
5 TCS Y ON
From the above records, I need to select all the records with source as IBM within that same ID group and other source should be also there for the same ID along with IBM.Also, we need to fetch only those records where at least one record in that ID group with curr_fl='Y'
In the above scenario even though the ID=1 have a source as IBM, but there is no record in that particular group with other source.So we shouldn't fetch that record with that ID.
For the record ID=3 have a source as IBM along with other sources, but there is no record with CURR_FL='Y', my query should not fetch the value.In the case of ID=4, it can fetch all the records with ID=4, as one of the records have value='Y' and it have a combination of IBM with other source.For ID 5 it should not fetch as we dont have any IBM record source within that set
Also within the group which has satisfied the above condition, I need one more condition for type. if there are records with type='P', then I need to fetch only that record.If there are no records with P, then I will search for type='OF' else type='ON'
My Expected output is given below
ID SOURCE CURR_FLAG TYPE
2 IBM Y P
2 TCS Y P
4 IBM NULL OF
4 CTS NULL OF
4 TCS Y ON
I have written a query as given below.But it's running for long and not fetching any results. Is there any better way to modify this query
select
ID,
SOURCE,
CURR_FL,
TYPE
from TABLE a where
exists(select 1 from TABLE B where a.ID=B.ID and a.rowid<>B.rowid and B.source<>a.source)
and exists(select 1 from TABLE C where a.ID=C.ID and C.source ='IBM')
and exists(select 1 from TABLE D where a.ID=D.ID and D.CURR_FL='Y') and
(TYPE,ID) IN (
select case type when 1 then 'P' when 2 then 'OF' else 'ON' END TYPE,ID from
(select ID,
max(priority) keep (dense_rank first order by priority asc) as type
from ( select ID,TYPE,
case TYPE
when 'P' then 1
when 'OF' then 2
when 'ON' then 3
end as priority
from TABLE where ID
in(select ID from TABLE where CURR_FL='Y') AND SOURCE='IBM'
)
group by ID))
First, look for the ids. I would recommend:
select id
from t
group by id
having min(source) <> max(source) and -- at least two sources
sum(case when curr_flag = 'Y' then 1 else 0 end) > 0 and -- at least one Y
sum(case when source = 'IBM' then 1 else 0 end) > 0 -- IBM
To get the base rows, you can use in, exists, or join:
select t.*
from t
where id in (select id
from t
group by id
having min(source) <> max(source) and -- at least two sources
sum(case when curr_flag = 'Y' then 1 else 0 end) > 0 and -- at least one Y
sum(case when source = 'IBM' then 1 else 0 end) > 0 -- IBM
);
Using analytic functions you can check that there is at least one IBM in the group, at least one other source apart from IBM in the group, and at least one flag with Y in the group:
with t(id, source, curr_flag, type) as
(
select 1, 'IBM', 'Y', 'P' from dual union all
select 1, 'IBM', 'Y', 'OF' from dual union all
select 1, 'IBM', 'Y', 'P' from dual union all
select 2, 'IBM', 'Y', 'P' from dual union all
select 2, 'TCS', 'Y', 'P' from dual union all
select 3, 'IBM', NULL, 'P' from dual union all
select 3, 'CTS', NULL, 'P' from dual union all
select 3, 'TCS', NULL, 'P' from dual union all
select 4, 'IBM', NULL, 'OF' from dual union all
select 4, 'CTS', NULL, 'OF' from dual union all
select 4, 'TCS', 'Y', 'ON' from dual union all
select 5, 'CTS', NULL, 'OF' from dual union all
select 5, 'TCS', 'Y', 'ON' from dual
)
select id, source, curr_flag, type
from (select id, source, curr_flag, type,
max(case when source = 'IBM' then 1 end) over (partition by id) ibm,
max(case when source != 'IBM' then 1 end) over (partition by id) not_ibm,
max(case when curr_flag = 'Y' then 1 end) over (partition by id) flag_y
from t)
where ibm = 1
and not_ibm = 1
and flag_y = 1
order by id, source;

Oracle Pivot Column in group query

I have the following working query:
WITH pivot_data AS (
select PSGROUP,
PSCOLUMN as PSCOLUMN
FROM LOG_PS_STATUS
)
SELECT *
FROM pivot_data
PIVOT (
MAX(NULL) --<-- pivot_clause
FOR PSCOLUMN--<-- pivot_for_clause
IN (&PS_COLUMNS.) --<-- pivot_in_clause
);
It shows results as expected:
Values:
PSGroup PSColumn
A 1
A 2
A 3
B 1
B 2
B 3
C 3
Result is giving like:
PSGroup(Column vertically) PSColoumn(Horizontally)
1 2 3
A
B
C
Now I want to make PSGroup column as group of PSColumn and output should be like:
A
1 2 3
B
1 2 3
C
3
You can use listagg:
WITH pivot_data(PSGroup,PSColumn) AS (
select 'A', 1 FROM dual UNION all
select 'A', 2 FROM dual UNION all
select 'A', 3 FROM dual UNION all
select 'B', 1 FROM dual UNION all
select 'B', 2 FROM dual UNION all
select 'B', 3 FROM dual UNION all
select 'C', 3 FROM DUAL),
res(PSGroup,PSColumns) as(
SELECT PSGroup, LISTAGG(PSColumn, ' ') WITHIN GROUP (order by PSColumn)
FROM pivot_data
GROUP BY PSGroup)
select DECODE(PSColumns,NULL,PSGroup,NULL) AS PSGroup, PSColumns from(
select PSGroup, NULL as PSColumns from res
union all
select PSGroup, PSColumns from res)t
ORDER BY t.PSGroup, t.PSColumns NULLS first
Also note that LISTAGG(PSColumn, ' ') WITHIN GROUP (order by PSColumn) is limited to 4000 characters

Oracle - How to return multi records from CASE

How to return multiple records from "CASE DEFAULT".
eg:
Master.COLUMN1 IN (CASE '#InputString'
WHEN 'One' THEN 1
WHEN 'Two' THEN 2
WHEN 'THREE' THEN 3
ELSE (SELECT NUM_BER FROM TABLE1 WHERE COLUMN2 LIKE '%#InputString%')
END)
I tried with passing One and it returns 1. But when I pass 'four' it showed error like ORA-01427 single-row sub query returns more than one row. How can i solve this??
you can try it like this:
column1 in (CASE '#InputString'
WHEN 'One' THEN 1
WHEN 'Two' THEN 2
WHEN 'THREE' THEN 3
END)
OR (column1 in (SELECT NUM_BER FROM TABLE1 WHERE COLUMN2 LIKE '%#InputString%')
and '#InputString' not in ('One', 'Two', 'THREE'));
Here is a sqlfiddle demo
You're getting the ORA-01427 error, because in select query in your else parts returns more than one row.
The below queries will help you:
WITH t AS
(SELECT 1 string FROM dual
UNION
SELECT 2 string FROM dual
UNION
SELECT 1234 string FROM dual
)
SELECT * FROM t
where t.string in ( case 'one' when 'one' then 1 else (select 1234 from dual) end);
output
_____
1
WITH t AS
(SELECT 1 string FROM dual
UNION
SELECT 2 string FROM dual
UNION
SELECT 1234 string FROM dual
)
SELECT * FROM t
where t.string in ( case 'two' when 'one' then 1 else (select 1234 from dual) end);
output
------
1234
You can use decode
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 'A' as string from dual union all
3 select 'B' from dual union all
4 select 'C' from dual union all
5 select 'D' from dual
6 )
7 select string , decode(string, 'A',1,'B',2,3) as string_out
8* from x
SQL> /
S STRING_OUT
- ----------
A 1
B 2
C 3
D 3

Resources