Using query results as In clause parameter - sql

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>

Related

get one value from multiple values in Oracle SQL

I tried the below query which returns two values but i need to get one value separated with ','. For example the query should return result :
TRPT,PRLD
Below is the query i tried:
SELECT
LISTAGG(T_NAME, ',') WITHIN GROUP (ORDER BY T_NAME) "ListValues"
FROM TST_TBL where T_DATE > sysdate-1
GROUP BY T_NAME
It returns:
ListValues
TRPT
PRLD
Remove the GROUP BY clause, it causes your "problems".
SQL> with tst_tbl (t_name, t_date) as
2 (select 'TRPT', sysdate from dual union all
3 select 'PRLD', sysdate from dual
4 )
5 SELECT
6 LISTAGG(T_NAME, ',') WITHIN GROUP (ORDER BY T_NAME) "ListValues"
7 FROM TST_TBL where T_DATE > sysdate-1
8 /
ListValues
--------------------
PRLD,TRPT
SQL>

SQL : How to find the count of an particular category values from an column with string values

I have a SQL Table called "category" looks like this
id | category
--------------
1 | 3,2
2 | 1
3 | 4,3,2
4 | 2,1
5 | 1,4
6 | 2,3,4
There are multiple category id's in the column "category", I need to find the count of an particular category values.
Current method I am using is:
select count(distinct(Category)) AS coldatacount from table_name
It gives the count of all the distinct values WHERE as I need to get
the count of all the particular category_id's separately.
if you are trying to get the Category Ids in comma delimited, you can use the string_split function to get distinct category_id
with cte as (
select 1 as id, '3,2' as category union all
select 2, '1' union all
select 3, '4,3,2' union all
select 4, '2,1' union all
select 5, '1,4' union all
select 6, '2,3,4'
)select count(distinct(value)) as category from cte
cross apply string_split(cte.category, ',');
I assumed that #neeraj04 may be looking for count of all Id in the category, continuing with #METAL code:
CREATE TABLE YourTable
(
Id INT IDENTITY,
[Category] VARCHAR(50)
);
INSERT YourTable VALUES ('3,2'), ('1'), ('4,3,2'), ('2,1'), ('1,4'), ('2,3,4');
SELECT CAST(value AS INT) AS category -- Value is string ouptut
, COUNT([value]) AS IdCount
FROM YourTable yt
CROSS APPLY string_split(yt.Category, ',')
GROUP BY [value]
ORDER BY category;
This is a horrible data model. You should not be storing multiple values in a string. You should not be storing numbers as strings.
Sometimes we are stuck with other people's really, really bad decisions. One approach is to split the string and count:
select t.*, cnt
from t cross apply
(select count(*) as cnt
from string_split(t.category) s
) s;
The other is to count commas:
select t.*,
(1 + len(t.category) - len(replace(t.category, ',', '')) as num_elements
Select Count(Value) from (Select Value from Table_Name a Cross Apply
string_split(a.Category, ','))ab Where Value=1

How to do sorting and then numbering on an Oracle database

As an example I have a database with the following information
Name Number
Boris
Trevor
Arthur
bessie
big Dave
BOB
I want to be able to sort that data in the below order and then add a number to the number column in that specific order
Name Number
Arthur 1
BOB 2
Boris 3
big Dave 4
bessie 5
Trevor 6
I can select using the order I have specified using
select DB.TABLE.NAME , case
when row_number() over(partition by lower(DB.TABLE.NAME )
order by DB.TABLE.NAME ) = 1
then 1
else 0
end as result
from DB.TABLE;
but I then have no idea how to apply the numbers to the numbers column.
If I try a different method of sorting, I can use a sequence to apply the numbers but the order is not what I want. It seems to be the row_number() function that is causing me problems.
Any help would be appreciated.
I think what you're after is something like:
with sample_data as (select 'Boris' name from dual union all
select 'Trevor' name from dual union all
select 'BO Derek' name from dual union all
select 'Arthur' name from dual union all
select 'big dave' name from dual union all
select 'big Dave' name from dual union all
select 'BOB' name from dual union all
select 'BORAT' name from dual union all
select 'Brian' name from dual union all
select 'Big Bad Dom' name from dual)
-- end of creating a subquery "sample_data" to mimic a table with data in it.
-- see SQL below:
select name,
row_number() over (order by upper(substr(name, 1, 1)),
name) row_num
from sample_data
order by upper(substr(name, 1, 1)),
name;
NAME ROW_NUM
----------- ----------
Arthur 1
BO Derek 2
BOB 3
BORAT 4
Big Bad Dom 5
Boris 6
Brian 7
big Dave 8
big dave 9
Trevor 10
To update a table, you'd do something like (assuming name is a unique column):
merge into some_table tgt
using (select name,
row_number() over (order by upper(substr(name, 1, 1)),
name) row_num
from some_table) src
on (tgt.name = src.name)
when matched then
update set tgt.number = src.row_num;
Use a MERGE statement:
merge into the_table t
using (
select rowid as rid,
row_number() over(order by lower(name)) as result
from the_table
) nr on (nr.rid = t.rowid)
when matched then update
set "number" = nr.result;
I am not sure what the CASE should do. It only returns 1 or 0 but the expected result shows you want numbers from 1 to 6, so I removed the CASE
If you have a proper primary key on the table, it's better to use that instead of rowid
Try this.
select DB.TABLE.NAME ,
row_number() over(ORDER by DB.TABLE.NAME ) as Number
from DB.TABLE
order by DB.TABLE.NAME;
Maybe you are looking to update db.table in that case:
update DB.TABLE
set number = (select row_number() over(ORDER by DB.TABLE.NAME ) as Number
from DB.TABLE t1 where t1.name = DB.TABLE.NAME );
Thanks all for your suggestions.
I went with this hacky approach to the answer by #a_horse_with_no_name
CREATE SEQUENCE NEWSEQ
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1;
merge into DB.TABLE t
using (
select rowid as rid, DB.TABLE.NAME, case
when row_number() over(partition by lower(DB.TABLE.NAME )
order by DB.TABLE.NAME ) = 1
then 1
else 0
end as result
from DB.TABLE
) nr on (nr.rid = t.rowid)
when matched then update
set NUMBER = NEWSEQ.NEXTVAL;
drop sequence NEWSEQ;
It may not be the most efficient way to do it, but it works

Oracle : Combine unique row values into comma-separated string [duplicate]

This question already has answers here:
How can I combine multiple rows into a comma-delimited list in Oracle? [duplicate]
(11 answers)
Closed 8 years ago.
I have a query that returns data like below, need to combine the value column into comma-separated string excluding duplicates and null values.
g_name g_id v_data
----- ---- ------
Test 123 ABC
Test 123 ABC
Test 123 DEG
Test 123 None
Test 123
Test 123 HIJ
Desired output :
g_name g_id v_data
----- ---- ------
Test 123 ABC,DEG,HIJ
I have tried using XMLAGG but can't remove duplicates and null values.
select g_name,
g_id,
RTRIM(XMLAGG(XMLELEMENT(e, v_data || ',')).EXTRACT('//text()'), ',')
from tblData
group by g_name, g_id
Just pre-filter your rows with a common table expression, then do your string concatenation.
with cte1 as (
select distinct *
from tblData
where v_data is not null
)
select g_name,
g_id,
RTRIM(XMLAGG(XMLELEMENT(e, v_data || ',')).EXTRACT('//text()'), ',')
from cte1
group by g_name, g_id
You can use the LISTAGG function:
select g_name, g_id, listagg(v_data,',') within group (order by v_data) v_data
from (
select distinct g_name, g_id, v_data
from tblData
where v_data is not null
)
group by g_name, g_id
In the inner select, we deal with the DISTINCT and NULL removal (you could also remove None here if you wanted).
The outer query deals with grouping by your name and id and concatenating the values

oracle pl/sql results into one string

I'm trying to create a simple stored procedure that stores queried result into one string.
v_string1 varchar2(100);
Select column1
From dual;
Will return
column 1
--------
aaaa
bbbb
cccc
I want to store "aaaa, bbbb, cccc' into v_string1.
And all I can think of is a Cursor...
Is there a better way to handle this?
Using SQL Fiddle:
select LISTAGG(name, ',') WITHIN GROUP (ORDER BY 1) AS names
from temp_table
Another option using pure SQL that will work before Oracle 11G, although is still limited to 4000 characters for the string.
Select ltrim(max(names), ', ') as names
From (
Select sys_connect_by_path(name, ' ,') as names
From (
Select name, row_number() over (order by name) as rown
From temp_table
)
Start with rown = 1
Connect by rown = prior rown + 1
)