How to use cursor with LISTAGG in PL/SQL? - sql

I have used LISTAGG to concatenate data from two different tables to form the following output:
How do I display the above output neatly like this:
I am using ORACLE PL/SQL. I am thinking if this can be done by implementing cursor, but I am not sure how to do it. Or maybe is there any other way to achieve this? Thanks.

Looks like NATION.N_NAME column's datatype is CHAR as those names are blank-padded. I'd switch to VARCAHR2 (if possible) or try with TRIM, e.g.
select ...
listagg(trim(n.n_name), ', ') within group ...
----
this

WITH CTE AS
(SELECT r.REGION_KEY
,r.R_NAME
,LIST_AGG(trim(n.N_NAME),',') WITHIN GROUP (ORDER BY R_NAME) AS REGION_NATION
FROM REGION r
INNER JOIN NATION n
ON r.R_REGION_KEY = n.N_REGIONKEY
GROUP BY r.R_REGION_KEY
,r.R_NAME
)
SELECT REGION_KEY
,R_NAME || ':' || REGION_NATION as REGION_TEXT
FROM CTE

Related

ListAgg Over ListAgg - Oracle

I wanted to get some data from multiple rows under same column (SRAV.XYZ) and concat it with other col hence used the listagg query.
SELECT LISTAGG (
REGEXP_SUBSTR (SRAV.XYZ,
'[^:]+$'),
';')
WITHIN GROUP (ORDER BY
REGEXP_SUBSTR (
SRAV.XYZ,
'[^:]+$')) ||';'||SRA.ABC
/*(CASE
WHEN SRA.ABC like 'PROF.TMP' THEN SRA.ABC = 'TMP'
WHEN SRA.ABC like 'PROF' THEN SRA.ABC ='PROF'
ELSE SRA.ABC='EMPLOYEES' END) */
FROM TEST1 SPAEM,
TEST2 SRAV,
TEST3 srm,
TEST4 SRA
WHERE SRAV.RID = srm.RGID
AND SRAV.PID IN
('123RTU23',
'456U43',
'AB4577Y')
AND SRAV.XYZ IS NOT NULL
AND SPAEM.EMPID = srm.SEC_UUID
AND SRAV.PID = SRA.PRID
AND SPAEM.EMPID = 139806
group by ABC
I am able to get the output in the below format:
physics;PROF.TMP
bio;EMPLOYEES
Now, I am having 2 issues that I am unable to handle.
I want the output in the below format:
physics;PROF.TMP,bio;EMPLOYEES
My case when is not working ( hence commented ) when I am trying to concat.
The ideal output would be:
physics;TMP,bio;EMPLOYEES
Any help in this regard.
Regards,
CASE probably doesn't work because of LIKE; the way you put it, it acts as if it was =, actually. Wildcards are missing. Also, syntax you used seems to be wrong (from my point of view). Perhaps you meant to say something like this:
CASE
WHEN SRA.ABC like '%PROF.TMP%' THEN 'TMP'
WHEN SRA.ABC like '%PROF%' THEN 'PROF'
ELSE 'EMPLOYEES'
END
As of listagg over listagg: use your current query as a subquery or as a CTE, and then apply yet another listagg:
with your_current_query as
(select listagg(...) within group over (...) as result_1
from ...
where ...
)
-- apply listagg to result_1
select listagg(result_1, ', ') over (...) as final_result
from your_current_query
That's theory. If you want something more, provide a simple test case.

Teradata Concatenate multiple rows using XMLAGG getting issue in XmlAgg function or any equivalent logic to concatendate multiple rows

I have a table of record tried to concatenate multiple rows on group wise and i use XMLAGG function but when i try to run the query for particular group which has 2000 records, getting error message:
Select failed 9134 : Intermediate aggregate storage limit for aggregation has been exceeded during computation
SELECT
H.GROUP_id,
H.Group_name,
TRIM(
TRAILING ',' FROM (
XMLAGG(TRIM(COALESCE(H.Group_desc, -1) || '') ORDER BY H.LINE_NBR) (VARCHAR(7000))
)
) AS Group_detail
even increased the varchar value but still having same issue
XMLAGG() adds overhead. However, you can get a sense for how large the result set is by using:
SELECT H.GROUP_id, H.Group_name,
SUM(LENGTH(COALESCE(H.Group_Desc, '-1'))) as total_string_length,
COUNT(*) as cnt
FROM . . .
GROUP BY H.GROUP_id, H.Group_name
ORDER BY total_string_length DESC
You will probably find that some of the groups have total string close to or more than 7000 characters.
I'm not sure if you want to fix the data or do something else. But this should at least identify the problem.
The problem is that the concatenation would be repeated for every row in the dataset, you need to get the distinct Group_desc first, try this:
WITH BASE AS(
SEL
H.GROUP_id,
H.Group_name,
H.Group_desc,
MAX(H.LINE_NBR) AS LINE_NBR
FROM TABLE_NAME
GROUP BY 1,2,3
)
SELECT
BASE.GROUP_id,
BASE.Group_name,
TRIM(
TRAILING ',' FROM (
XMLAGG(TRIM(COALESCE(BASE.Group_desc, -1) || '') ORDER BY BASE.LINE_NBR) (VARCHAR(7000)) -- You probably won't need the varchar to be that large.
)
) AS Group_detail
FROM BASE

Unable to translate BigQuery legacy SQL to standard SQL for HAVING LEFT(...)

I would like to use BigQuery Standard SQL for a query like this one:
SELECT package, COUNT(*) count
FROM (
SELECT REGEXP_EXTRACT(line, r' ([a-z0-9\._]*)\.') package, id
FROM (
SELECT SPLIT(content, '\n') line, id
FROM [github-groovy-files:github.contents]
WHERE content CONTAINS 'import'
HAVING LEFT(line, 6)='import' )
GROUP BY package, id
)
GROUP BY 1
ORDER BY count DESC
LIMIT 30;
I cannot get past something like this (works but doesn't GROUP or COUNT):
with lines as
(SELECT SPLIT(c.content, '\n') line, c.id as id
FROM `<dataset>.contents` c, `<dataset>.files` f
WHERE c.id = f.id AND f.path LIKE '%.groovy')
select
array(select REGEXP_REPLACE(l, r'import |;', '') AS class from unnest(line) as l where l like 'import %') imports, id
from lines;
LEFT() is not in Standard SQL and there doesn't seem to be a function that will accept and array type.
LEFT() is not in Standard SQL ...
In BigQuery Standard SQL you can use SUBSTR(value, position[, length]) instead of Legacy's LEFT
... and there doesn't seem to be a function that will accept and array type.
There are plenty of Array's related functions as well as functions that accept array as argument - for example UNNEST()
I would like to use BigQuery Standard SQL for a query like this one:
Below is equivalent query for BigQuery Standard SQL
SELECT package, COUNT(*) COUNT
FROM (
SELECT REGEXP_EXTRACT(line, r' ([a-z0-9\._]*)\.') package, id
FROM (
SELECT line, id
FROM `github-groovy-files.github.contents`,
UNNEST(SPLIT(content, '\n')) line
WHERE SUBSTR(line, 1, 6)='import'
)
GROUP BY package, id
)
GROUP BY 1
ORDER BY COUNT DESC
LIMIT 30
Instead of WHERE SUBSTR(line, 1, 6)='import' you can use WHERE line LIKE 'import%'
Also note, this query can be written in number of ways - so in my above example I focused on "translating" your query into from legacy to standard sql while preserving core structure and approach of original query
But if you woukld like to rewrite it using power of Standard SQL - you would ended up with something like below
SELECT REGEXP_EXTRACT(line, r' ([a-z0-9\._]*)\.') package, COUNT(DISTINCT id) count
FROM `github-groovy-files.github.contents`,
UNNEST(SPLIT(content, '\n')) line
WHERE line LIKE 'import%'
GROUP BY 1
ORDER BY count DESC
LIMIT 30

SQL special group by on list of strings ending with *

I would like to perform a "special group by" on strings with SQL language, some ending with "*". I use postgresql.
I can not clearly formulate this problem, even if I have partially solved it, with select, union and nested queries which are not elegant.
For exemple :
1) INPUT : I have a list of strings :
thestrings
varchar(9)
--------------
1000
1000-0001
1000-0002
2000*
2000-0001
2000-0002
3000*
3000-00*
3000-0001
3000-0002
2) OUTPUT : That I would like my "special group by" return :
1000
1000-0001
1000-0002
2000*
3000*
Because 2000-0001 and 2000-0002 are include in 2000*,
and because 3000-00*, 3000-0001 and 3000-0002 are includes in 3000*
3) SQL query I do :
SELECT every strings ending with *
UNION
SELECT every string where the begining NOT IN (SELECT every string ending with *) <-- with multiple inelegant left functions and NOT IN subqueries
4) That what I'm doing return :
1000
1000-0001
1000-0002
2000*
3000*
3000-00* <-- the problem
The problem is : 3000-00* staying in my result.
So my question is :
How can I generalize my problem? to remove all string who have a same begining string in the list (ending with *) ?
I think of regular expressions, but how to pass a list from a select in a regex ?
Thanks for help.
Select only strings for which no master string exists in the table:
select str
from mytable
where not exists
(
select *
from mytable master
where master.str like '%*'
and master.str <> mytable.str
and rtrim(mytable.str, '*') like rtrim(master.str, '*') || '%'
);
Assuming that only one general pattern can match any given string, the following should do what you want:
select coalesce(tpat.thestring, t.thestring) as thestring
from t left join
t tpat
on t.thestring like replace(tpat.thestring, '*', '%') and
t.thestring <> tpat.thestring
group by coalesce(tpat.thestring, t.thestring);
However, that is not your case. However, you can adjust this with distinct on:
select distinct on (t.thestring) coalesce(tpat.thestring, t.thestring)
from t left join
t tpat
on t.thestring like replace(tpat.thestring, '*', '%') and
t.thestring <> tpat.thestring
order by t.thestring, length(tpat.thestring)

SQL limiting Count function based on IN clause using comma delimited list

I have a table of Matricies (JobMatricies) ID, Desc, DeptIDs
1 Admin (PM) 6,7,138,131,11,9,10,134,135,14,105,129
5 Sales Processing (PM) 92,16,153,17,91,32,26,93,99,18,89,90,155,19
6 Construction Processing (PM) 100,36,20,136,22,88,23,25,34,106,38,39,132,41,42,43,154,152,84
DeptIDs are a Comma Delimited list of departments that I want to use to count how many records are represented by the Matrix.
Normally I would do something like....
select Matrix_ID,
Matrix_Desc,
JobCount = (select count(sched_ID) from JobSchedule where dept_ID in (**92,16,153,17,91,32,26,93,99,18,89,90,155,19**))
from jobMatrices
How do I replace the hard coded delimited string with the ID's stored with each matrix, so that I can produce a list of matricies with their own unique count based on the comma delimited string that is stored with each matrix.
Thanks
I just answered a similar question where the poster wanted to sum the delimited list of numbers in a similar table layout. My solution used CTE's in Oracle to turn that list into a CTE table which would allow you to join against it. I believe that technique would be of use here if your RDBMS supports that. Please have a look: https://stackoverflow.com/a/38231838/2543416
select s.ID
, m."Desc"
from JobSchedule s
, jobMatrices m
where position(',' || s.ID || ',' in ',' || m.DeptIDs || ',')>0;
select m."Desc"
, count(s.ID) JobCount
from JobSchedule s
, jobMatrices m
where position(',' || s.ID || ',' in ',' || m.DeptIDs || ',')>0
group by m."Desc";
In MySQL, you can treat a comma-separated string as a SET, and there's a builtin function FIND_IN_SET() that helps:
select m.Matrix_ID,
m.Matrix_Desc,
(select count(sched_ID) from JobSchedule
where FIND_IN_SET(dept_ID, m.DeptIds)) AS JobCount
from jobMatrices AS m;
This will have terrible performance, however.
If you use some RDBMS other than MySQL, they may have a different solution that works similarly.
You should be specific in your question and tag your question appropriately. You only tagged your question sql, but this is a language used by many RDBMS vendors.