Convert column into rows in oracle - sql

I have table structure as below, Need the output as mentioned below
Table :
A B
CUSTOMER_TYPE_ID 4
CUSTOMER_TYPE_ID 3
CUSTOMER_TYPE_ID 2
CUSTOMER_TYPE_ID 1
CUSTOMER_TYPE_ID 0
Answer :
'4','3','2','1','0'
How to do it?

As described, this isn't a pivot but aggregate string concatenation. The Oracle function is LISTAGG():
select listagg(b, ',') within group (order by b desc) as b
from t
group by a;
EDIT:
If you want single quotes around the values:
select listagg('''' || b || '''', ',') within group (order by b desc) as b
from t
group by a;

Related

Using query results as In clause parameter

I know I've seen this before, but can't come up with the search terms to find it.
I have a CTE returning a comma separated list from the below table:
Table
Create table Table
(
ID Number
, Name varchar2(100)
);
insert all
into Table (ID, Name) values (1, 'Alex')
into Table (ID, Name) values (2, 'Amy')
into Table (ID, Name) values (3, 'Jim')
select * from dual;
ID
Name
1
Alex
2
Amy
3
Jim
select substr(
listagg(Table.ID || ',') within group (order by null)
, 1
, length(listagg(Table.ID || ',') within group (order by null)) - 1
) IDs
from Table
where Name like 'A%'
Which gives me the results: 1,2
I'm trying to use this result in a query's in clause:
with CTE as
(
select substr(
listagg(tbl.ID || ',') within group (order by null)
, 1
, length(listagg(tbl.ID || ',') within group (order by null)) - 1
) IDs
from Table
where Name like 'A%'
)
select *
from Table
where cast(ID as varhcar2(1000)) in (select IDs from CTE) --Use results here
--believe the cast is required to compare, otherwise get a ORA-01722: invalid number
Which I want to return:
ID
Name
1
Alex
2
Amy
How can I use the CTE's resulting IDs string as the parameter of my in clause?
I'm afraid I don't understand your "problem". CTE is really strange; SUBSTR of something? Why? LISTAGG returns the same result anyway. Then you want to ... what? split that result so that you could use it in another query? As if you want to make it as complex as possible (and beyond) to solve something "simple". Therefore: what real problem are you trying to solve?
Anyway, here you go: you'll have to split aggregated string into rows if you want to use it in IN clause:
SQL> with CTE as
2 (select listagg(ID || ',') within group (order by null) IDs
3 from Test
4 where Name like 'A%'
5 )
6 select *
7 from Test
8 where id in (select regexp_substr(IDs, '[^,]+', 1, level)
9 from CTE
10 connect by level <= regexp_count(IDS, ',') + 1
11 );
ID NAME
---------- ----------
1 Alex
2 Amy
SQL>
The same result is returned by a simple
SQL> select *
2 from Test
3 where Name like 'A%';
ID NAME
---------- ----------
1 Alex
2 Amy
SQL>
That's why I asked: what problem are you trying to solve?
[EDIT] As of trailing comma: there's none, at least not any Oracle version I used (11g, 12c, 18cXE, 21cXE):
SQL> select listagg(id, ',') within group (order by null) result from test;
RESULT
------------------------------
1,2,3
SQL> select listagg(name, ',') within group (order by null) result from test;
RESULT
------------------------------
Alex,Amy,Jim
SQL>

SQL: histograms for multiple columns

Given the following table:
Column A Column B
east red
west blue
east green
I want to find out the of column values of each column and how many times each value is present in the table. Given the output above the result should look like:
A values A value counts B values B value counts
east 2 red 1
west 1 blue 1
green 1
This is achievable by running SELECT colX, count(colX) From Table GROUP BY colX for each column. This is not a scalable solution if there is a complex WHERE condition since it needs to be executed for each query.
An alternative is to execute the complex where query once and compute the aggregations in the server code. But is there a single SQL query that can compute that?
You can use window function :
select cola, count(*) over (partition by cola) as a_count,
colb, count(*) over (partition by colb) as b_count
This will count for both columns (a & b) with their values display.
You can use subqueries to aggregate, then union all and aggregate again to combine the results:
select max(a) as a, max(a_cnt) as a_cnt, max(b) as b, max(b_cnt) as b
from ((select a, count(*) as a_cnt, null as b, null as b_cnt,
row_number() over (order by count(*) desc) as seqnum
from t
group by a
) union all
(select null, null, b, count(*),
row_number() over (order by count(*) desc) as seqnum
from t
group by b
)
) ab
group by seqnum
order by seqnum;
If you are using Oracle you can use user_tab_cols to generate the SQL for all columns in your table
SELECT 'SELECT '
|| Listagg(column_name
||',count(1) over (partition by '
||column_name
||') as '
||column_name
||'_cnt', ',')
within GROUP (ORDER BY column_id)
||' FROM '
||'TEST_DATA'
FROM user_tab_cols
WHERE table_name = 'TEST_DATA'
Sample output is below
SELECT ID,count(1) over (partition by ID) as ID_cnt,VALUE,count(1) over (partition by
VALUE) as VALUE_cnt FROM TEST_DATA

Oracle: SQL Dynamic cursor statement

I have a dynamic temporary table like below.
Table name for assumption: TB_EMP_TEMP_TABLE
Column1 | column2 | column3
Emp_NM | EMP_ID |TB_EMP_DTLS
Emp_Adr | EMP_ID |TB_EMP_DTLS
Emp_Sal | EMP_ID |TB_EMP_OTHER
The above data is retrieved as a Cursor(Emp_cursor) and i need to construct a dynamic SQL Query as below based on cursor data.
Expected Output:
SELECT TB_EMP_DTLS.EMP_NM,TB_EMP_DTLS.EMP_Adr,TB_EMP_OTHER.EMP_SAL
FROM TB_EMP_DTLS,TB_EMP_OTHER
WHERE TB_EMP_DTLS.EMP_ID=TB_EMP_OTHER.EMP_ID
I havent worked extensively on PLSQL/Cursor concepts. How the cursor can be looped to get expected output.
If i understand it right, you want column1 values selected from column3 tables joined by column2 columns.
It's not elegant but should work:
select listagg(v, ' ') within group (order by n asc) my_cursor from (
with
tb as (select distinct column3 val from tb_emp_temp_table), --tables
sl as (select distinct column3||'.'||column1 val from tb_emp_temp_table), --selected columns
pr as (select distinct column3||'.'||column2 val from tb_emp_temp_table) --predicates
select 1 n, 'SELECT' v from dual
union
select 2 n, listagg(val, ', ') within group (order by val) v from sl
union
select 3 n, 'FROM' v from dual
union
select 4 n, listagg(val, ', ') within group (order by val) v from tb
union
select 5 n, 'WHERE' v from dual
union
select 6 n, listagg(pra.val||'='||prb.val, ' AND ') within group (order by pra.val) v from pr pra, pr prb where pra.val != prb.val
)

SQL: Concatenate sequential integer values

I have a column like this:
ID
--------
1
2
3
4
5
7
10
and I want to get the following resultset:
ID
--------
1-5
7
10
Is there a way to achieve this with (Oracle) SQL only?
Yes:
select (case when min(id) < max(id)
then cast(min(id) as varchar2(255)) || '-' || cast(max(id) as varchar2(255))
else cast(min(id) as varchar2(255))
end)
from (select id, id - rownum as grp
from t
order by id
) t
group by grp
order by min(id);
Here is a SQL Fiddle demonstrating it.
The idea behind the query is that subtracting rownum from a sequence of numbers results in a constant. You can use the constant for grouping.
self joins are necessary... I think this will work
Select a.id, b.id
From table a -- to get beginning of each range
Join table b -- to get end of each range
On b.id >= a.Id -- guarantees that b is after a
And Not exists (Select * From table m -- this guarantees all values between
where id Between a.id+1 and b.id
And Not exists(Select * From table
Where id = m.id-1))
And Not exists (Select * From table -- this guarantees that table a is start
Where id = a.id -1)
And Not exists (Select * From table -- this guarantees that table b is end
Where id = b.id + 1)

Eliminate duplicates using Oracle LISTAGG function [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LISTAGG in oracle to return distinct values
I am using Oracle LISTAGG function but within my list of returned names I actually would like to eliminate duplicates and only return distinct values.
The query I have is something like this:
select a.id,
a.change_id,
LISTAGG(b.name, ',') WITHIN GROUP (ORDER BY b.name) AS "Product Name",
from table_a a,
table_b b
where a.id = 1
and b.change_id = c.change_id
group by a.id, a.change_id
At the moment, it is returning (just showing one record):
1 1 NameA, NameA, NameB, NameC, NameD, Name D
What I would like returned is:
1 1 NameA, NameB, NameC, Name D
As the linked answers in the comment don't provide my flavor of solution, I'll post it anyway.
I'll only use table_b with dummy data to show the concept, you can easily add your join etc.:
with table_b as ( -- dummy data
select 'name'||mod(level,3) name
,mod(level,3) id
from dual
connect by level < 10
union all
select 'name'||mod(level,2) name
,mod(level,3) id
from dual
connect by level < 10
)
select id
,RTRIM (
XMLAGG (
XMLELEMENT (E,XMLATTRIBUTES (name|| ',' AS "Seg")
)
ORDER BY name ASC
).EXTRACT ('./E[not(#Seg = preceding-sibling::E/#Seg)]/#Seg'),
','
) AS "Product Name"
,LISTAGG(b.name, ',') WITHIN GROUP (ORDER BY b.name) AS "Product Name with dups"
from table_b b
group by id;
(Idea taken from https://forums.oracle.com/forums/thread.jspa?messageID=9634767&tstart=0#9943367)