subquery eror and too many values using xmllagg - sql

I have issue concatenating all of my data from many large tables. I asked question yesterday regarding this but unfortunately seems listagg is not good option. the link subquery return more than one row
I tried to use the xmllagg after listagg using to truncate is not possible with my version of oracle 12.0.1 , the first code as seen below is given subquery is returning more than one row...
SELECT rtrim(xmlagg(XMLELEMENT(e,table1.DESCRIPTION,',').EXTRACT ('//text()')
).GetClobVal(),',')
FROM table1
left join table2 on table1.app = table2.app
AND LANGUAGE = 2
GROUP BY table2.app
The second code one using another method is still saying too many values in first line
SELECT nvl(max(case when language = 2 then description end), 'NULL') key_event, rtrim(xmlagg(XMLELEMENT (e,table1.DESCRIPTION,',').EXTRACT ('//text()')
).GetClobVal(),',')
FROM table1
left join table2 on table1.app = table2.app
AND LANGUAGE = 2
GROUP BY table2.app
I have tested these 2 codes in this link dbfiddle and it was working. I want the result to be as this link
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=40852eaeaa8f334f77364eef77ffbe68
I did get result testing it as small bit but when i try to use the full code just look at this code, it is not working please see the code and error below error :subquery return more than one row
SELECT
NVL(TO_CHAR(D_TRANS.TRANS), 'NULL') AS ID,
'HEADER'
, (SELECT L_APPLICATION.APPLICATION FROM L_APPLICATION L_APPLICATION WHERE LANGUAGE = 2 AND APPLICATION = D_TRANS.APPLICATION) AS CASE_TYPE
, NVL(TO_CHAR(D_TRANS.UNIT_IN_CHARGE), 'NULL') AS UNIT_IN_CHARGE
, NVL(TO_CHAR(D_TRANS.PERSON_IN_CHARGE), 'NULL') AS PERSON_IN_CHARGE
, NVL(TO_CHAR(D_TRANS.STATUS), 'NULL') AS CASE_STATUS
, NVL(TO_CHAR(D_TRANS.DEADLINE), 'NULL') AS INTERNAL_DEADLINE
(SELECT D_SYNERGI_CATEGORY.TRANS, nvl(max(case when language = 2 then description end), 'NULL') AS ADE , rtrim(xmlagg(XMLELEMENT (e,L_CASE_CATEGORY.DESCRIPTION,',').EXTRACT('//text()')
).GetClobVal(),',')
FROM L_CASE_CATEGORY
left join D_SYNERGI_CATEGORY on D_SYNERGI_CATEGORY.CASE_CATEGORY = L_CASE_CATEGORY.CASE_CATEGORY
GROUP BY D_SYNERGI_CATEGORY.TRANS
)
)
FROM D_TRANS
FULL OUTER JOIN D_SPILL
ON D_TRANS.TRANS=D_SPILL.TRANS
ORDER BY D_TRANS.TRANS DESC;
When I tested this small bit of this code below it is working.
(SELECT D_SYNERGI_CATEGORY.TRANS, nvl(max(case when language = 2 then description end), 'NULL') AS ADE , rtrim(xmlagg(XMLELEMENT(e,L_CASE_CATEGORY.DESCRIPTION,',').EXTRACT('//text()')
).GetClobVal(),',')
FROM L_CASE_CATEGORY
left join D_SYNERGI_CATEGORY on D_SYNERGI_CATEGORY.CASE_CATEGORY = L_CASE_CATEGORY.CASE_CATEGORY
GROUP BY D_SYNERGI_CATEGORY.TRANS
)
I am not expert in Oracle. Any suggestion ?

Combination of xmlagg and xQuery, it's not simple.
select xmlquery('distinct-values(//text())' passing x returning content).getclobVal(),data_type from (
select xmlelement(root, xmlagg(XMLELEMENT(e,table_name,','))) x ,data_type
from user_tab_cols where data_type in ('VARCHAR2','NUMBER')
group by data_type
)
And for you purpose it should look like this
select
app, key_event,
xmlquery('distinct-values(//text())' passing xmldoc returning content).getclobVal()
from
(select
t2.app,
coalesce(max(case when language = 2 then description end),
max(case when language = 12 then description end),
max(case when language = 27 then description end),
'NULL') key_event,
XMLELEMENT(root, xmlagg(XMLELEMENT(e, description, ','))
) xmldoc
from
table2 t2
left join
table1 t1 on t1.app = t2.app
group by
trans, t2.app
order by trans);
if you will decompose this query you will see how its work.
xquery distinct-values syntax
xmlquery syntax

with REGEXP_REPLACE we can make deduplication. Check this:
select t2.app,
coalesce(max(case when language = 2 then description end),
max(case when language = 12 then description end),
max(case when language = 27 then description end),
'NULL') key_event,
REGEXP_REPLACE(listagg(description, ',') within group (order by t1.description, t1.language), '([^,]+)(,\1)+', '\1') list
from table2 t2
left join table1 t1 on t1.app = t2.app
group by trans, t2.app
order by trans, trans;

Related

SQL query row_number() over partition by query result return wrong for some case

I have two SQL Server tables:
FormSchema columns
SchemaId, SchemaName, Tenant
RoleTable columns
SchemaId, RoleId, RoleName, Tenant
Expected result (includes below three conditions):
select row in result where individual SchemaName exist in Tenant 'A'
select row in result where individual SchemaName exist in Tenant 'All'
If duplicate/same SchemaName exist in Tenant 'A' and 'All' both then select row in result which belong to Tenant 'A'.
Query:
select *
from
(select
fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant,
rn = row_number() over (partition by fs1.schemaName
order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant)
from
RolesTable ar1
full outer join
FormSchema fs1 on ar1.SchemaId = fs1.SchemaId) as t3
where
rn = 1
and Tenant in ('B', 'All')
Please see the db fiddle for table, records, and expected and actual result.
The issue I am facing like while giving Tenant as 'B' expected result is wrong but instead if i use 'A' expected result is correct.
Expected result for 'B' is wrong** because it's not satisfying the following:
If duplicate/same SchemaName exist in Tenant 'B' and 'All' both then select row in result which belong to Tenant 'B'.i.e. SchemaName 'Car'. So query should select in result 'B' 'Car' row.
Expected Result for 'B':
SchemaId RoleId RoleName Tenant
--------------------------------------------------------------------
'664' '40ecca83-7fd9-4d63-9f56-c7a48442d844' '#Test-1' 'B'
'456' '40ecca83-7fd9-4d63-9f56-c7a48442d844' '#Test-1' 'B'
Thanks in advance.
You should move the condition for the Tenant from the outer query inside the subquery:
select *
from (
select fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant,
row_number() over (
partition by fs1.schemaName
order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant
) rn
from RolesTable ar1 full outer join FormSchema fs1
on ar1.SchemaId= fs1.SchemaId
where ar1.Tenant in (?, 'All')
) as t3
where rn = 1
Replace ? with 'A' or 'B'.
Also, it is not clear why you are doing a FULL join instead of a LEFT join.
If you want in the results unmatched rows from both tables, then may be you should use COALESCE():
where coalesce(ar1.Tenant, fs1.Tenant) in (?, 'All')
See the demo.
I find the logic a bit hard to follow. And I don't understand why you are using a full join when an inner join does the work.
However, I think what you need to do is filter in the subquery:
select *
from (select fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant ,
row_number() over (partition by fs1.schemaName
order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant) as seqnum
from RolesTable ar1 join
FormSchema fs1
on ar1.SchemaId= fs1.SchemaId
where ar1.Tenant in ('B', 'All')
) t3
where seqnum = 1 ;

how to join multiple select statement with full outer join. Error: subquery is returning more than one row

I have this code have been battling with since yesterday, when I unit test each part of this code, it is working, but I need to put them together to generate one output result. This is the full code below : but is giving subquery is returning more than one row.
SELECT NVL(TO_CHAR(D_TRANS.TRANS), 'NULL') AS ID, 'HEADER', D_SPILL.status,
(SELECT L_APPLICATION.APPLICATION
FROM L_APPLICATION L_APPLICATION
WHERE LANGUAGE = 2 AND APPLICATION = D_TRANS.APPLICATION)
AS CASE_TYPE,
NVL(TO_CHAR(D_TRANS.UNIT_IN_CHARGE), 'NULL') AS UNIT_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.PERSON_IN_CHARGE), 'NULL') AS PERSON_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.STATUS), 'NULL') AS CASE_STATUS,
NVL(TO_CHAR(D_TRANS.DEADLINE), 'NULL') AS INTERNAL_DEADLINE,
( select xmlquery('distinct-values(//text())' passing xmldoc returning content).getclobVal()
FROM ( select d_synergi_category.trans,
coalesce(max(case when language = 2 then description end), 'NULL'),
XMLELEMENT(root,xmlagg(XMLELEMENT(e,description,','))
) xmldoc
from L_CASE_CATEGORY
LEFT JOIN d_synergi_category ON d_synergi_category.case_category = L_CASE_CATEGORY.case_category
group by d_synergi_category.trans
)
)
FROM D_TRANS
FULL OUTER JOIN D_SPILL
ON D_TRANS.TRANS=D_SPILL.TRANS
ORDER BY D_TRANS.TRANS DESC;
If I remove the part code below with xmltagg and test both parts of the code separately it is working.
First part working separately
( select xmlquery('distinct-values(//text())' passing xmldoc returning content).getclobVal()
FROM ( select d_synergi_category.trans,
coalesce(max(case when language = 2 then description end), 'NULL'),
XMLELEMENT(root,xmlagg(XMLELEMENT(e,description,','))
) xmldoc
from L_CASE_CATEGORY
LEFT JOIN d_synergi_category ON d_synergi_category.case_category = L_CASE_CATEGORY.case_category
group by d_synergi_category.trans
)
)
Second part working separately is :
SELECT NVL(TO_CHAR(D_TRANS.TRANS), 'NULL') AS ID, 'HEADER',D_SPILL.status,
(SELECT L_APPLICATION.APPLICATION FROM L_APPLICATION
WHERE L_APPLICATION WHERE LANGUAGE = 2
AND APPLICATION = D_TRANS.APPLICATION) AS CASE_TYPE ,
NVL(TO_CHAR(D_TRANS.UNIT_IN_CHARGE), 'NULL') AS UNIT_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.PERSON_IN_CHARGE), 'NULL') AS PERSON_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.STATUS), 'NULL') AS CASE_STATUS ,
NVL(TO_CHAR(D_TRANS.DEADLINE), 'NULL') AS INTERNAL_DEADLINE
FROM D_TRANS
FULL OUTER JOIN D_SPILL
ON D_TRANS.TRANS=D_SPILL.TRANS
ORDER BY D_TRANS.TRANS DESC;
Query (SELECT rtrim(xmlagg( ... must return exactly one row.
We don't have your tables nor data, but it seems that you didn't join its tables (L_CASE_CATEGORY, D_SYNERGI_CATEGORY) with any of tables contained in main query's FROM clause (TRANS, D_SPILL). I suggest you do that and see what happens.
[EDIT]
This is what I meant:
select nvl(to_char(d_trans.trans, 'null') as id,
...,
rtrim(xmlagg(xmlelement(...)) as some_name --> XLM stuff goes here
from l_case_category left join d_synergi_category on ...
join l_case_category on ... --> XML subquery's tables go here,
--> properly joined to other tables
What #Littlefoot said, but I'm going to take a guess at how your tables might be joined, so that you have an example.
SELECT NVL(TO_CHAR(D_TRANS.TRANS), 'NULL') AS ID, 'HEADER',D_SPILL.status,
(SELECT L_APPLICATION.APPLICATION FROM L_APPLICATION
WHERE L_APPLICATION WHERE LANGUAGE = 2
AND APPLICATION = D_TRANS.APPLICATION) AS CASE_TYPE ,
NVL(TO_CHAR(D_TRANS.UNIT_IN_CHARGE), 'NULL') AS UNIT_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.PERSON_IN_CHARGE), 'NULL') AS PERSON_IN_CHARGE,
NVL(TO_CHAR(D_TRANS.STATUS), 'NULL') AS CASE_STATUS ,
NVL(TO_CHAR(D_TRANS.DEADLINE), 'NULL') AS INTERNAL_DEADLINE,
(SELECT rtrim(xmlagg(
XMLELEMENT(e,L_CASE_CATEGORY.DESCRIPTION,',').EXTRACT('//text()')
).GetClobVal(),',')
FROM L_CASE_CATEGORY
INNER JOIN D_SYNERGI_CATEGORY on -- changed
D_SYNERGI_CATEGORY.CASE_CATEGORY = L_CASE_CATEGORY.CASE_CATEGORY
AND L_CASE_CATEGORY.LANGUAGE = 2
WHERE d_synergi_category.trans = D_TRANS.TRANS -- added this line
GROUP BY D_SYNERGI_CATEGORY.CASE_CATEGORY, d_synergi_category.trans) AS CAT_DESC_LIST
FROM D_TRANS
FULL OUTER JOIN D_SPILL
ON D_TRANS.TRANS=D_SPILL.TRANS
ORDER BY D_TRANS.TRANS DESC;
Edit: updated from your comment. This would be a lot easier if you provided table structure and example data.
Your subquery SELECT rtrim(xmlagg(... returns one row per CASE_CATEGORY and trans, because of:
GROUP BY D_SYNERGI_CATEGORY.CASE_CATEGORY, d_synergi_category.trans
When you put it in the main query's select clause, however, you don't want it to return all those rows per main query row, but the one row matching the main query row (just like you find the matching application row with WHERE APPLICATION = D_TRANS.APPLICATION).
So remove the GROUP BY clause and replace it with the WHERE clause instead. Something like
(
SELECT
RTRIM(XMLAGG(
XMLELEMENT(e, cc.description,',').EXTRACT('//text()')
).GetClobVal(),',')
FROM l_case_category cc
JOIN d_synergi_category sc ON sc.case_category = cc.case_category
WHERE sc.case_category = d_spill.case_category -- <=== here
AND sc.trans = d_trans.trans -- <=== and here
and cc.language = 2
)
(Just replace my criteria with your real criteria. Only you know how the tables are related. It is hard for me to even figure it out what the query is supposed to return. I don't understand why you full outer join with D_SPILL in one of the separate queries, without using any of its columns in the select clause. I don't know either why you outer join D_SYNERGI_CATEGORY on L_CASE_CATEGORY.LANGUAGE = 2 in the other query. This seems strange.)

Case returns more than one value with join

I have a problem when I'm using case statement with a join.
I have two tables. Tbl_a:
and Tbl_b:
I'm running the following query:
SELECT
tbl_a.id,
(
CASE
WHEN tbl_b.param_type = 'Ignition' Then param_value
WHEN tbl_b.param_type = 'Turn' Then param_value
WHEN tbl_b.param_type = 'Speed' Then param_value
WHEN tbl_b.param_type = 'Break' Then param_value
END
) as value
FROM
public.tbl_a
JOIN public.tbl_b on tbl_b.id = tbl_a.id
I want to get for each id in tbl_a the first match from tbl_b. If there is an id in tbl_b that his paramtype is 'Ignition' then I want to get his value. If there isn't, then I want to get the value of the paramtype 'Turn' and so on. But when I run the query I get all possible matches:
I understand why I get those results, but I don't know how to get the results I want.
Any help will be appreciated, thanks!
I think this approach does what you want:
SELECT a.id,
COALESCE( MAX(CASE WHEN b.param_type = 'Ignition' THEN a.param_value END),
MAX(CASE WHEN b.param_type = 'Turn' THEN a.param_value END),
MAX(CASE WHEN b.param_type = 'Speed' THEN a.param_value END),
MAX(CASE WHEN b.param_type = 'Break' THEN a.param_value END)
) as value
FROM public.tbl_a a JOIN
public.tbl_b
ON b.id = a.id
GROUP BY a.id;
You could GROUP and pull the first item...
SELECT
id,
FIRST(value)
FROM
public.tbl_a AS a
INNER JOIN
public.tbl_b AS b
ON a.id = b.id
GROUP BY
a.id
The problem you'll have here, though, is that there is no guarantee that your values are always going to be in the correct order unless there's some kind of ordering mechanism, such as a time or incremental ID.
NOTE:
I've written this for standard T-SQL, so if Amazon RedShift doesn't quite follow the standard, you may need to look up the syntax.

Remove repeated values from the table

I have a PL/SQL select query like,
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
Right now it retrieves like ,
sgm-|-numbr
-----------
abc-|-123
abc-|-678
abc-|-78
abc-|-099
bcd-|-153
bcd-|-123
bcd-|-123
I need to retrieve like ,
sgm-|-numbr
-----------
abc-|-123
bcd-|-153
ie, I need to remove the repeated ones in the first column. ie sgm shouldn't repeat.
Since you are using Oracle, then try this simplified version using a CTE:
WITH CTE as (
SELECT sgm, numbr,
rownum rn
FROM YourTable
)
SELECT CTE.sgm, CTE.numbr
FROM CTE
JOIN (
SELECT sgm, MIN(rownum) minrn
FROM CTE
GROUP BY sgm) t ON CTE.sgm = t.sgm AND CTE.rn = t.minrn
http://sqlfiddle.com/#!4/8d6fb/10
You can replace your query in the CTE above.
Good luck.
SELECT a.sgm, MAX(b.numbr)
FROM tbl1 a INNER JOIN tbl2 b
ON a.itemID = b.itemId
AND a.orgId = b.orgId
WHERE a.srvCode= 'F'
AND a.nbrCode <> 1
AND rownum <= 7
GROUP BY a.sgm
Apply a group function of your choice like MAX() on b.numbr, and apply the grouping on a.sgm, this should do what you need.
Advice : do your joins explicitly, see the difference between your query and mine.
select a.sgm,MAX(b.numbr)
from tbl1 a, tbl2 b
where b.itemId = a.itemId
AND b.orgId= a.orgId
and a.srvCode= 'F'
and a.nbrCode <> 1
and rownum<=7
group by sgm
The value of sgm wont repeat, but maximum value of number will be selected, similarly you can also select the minimum value using the Min function
Use group by function
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
group by a.sgm
Select a from tbl a , tbl b WHERE a.userid > b..userid and
a.sgm = b.sgm;
Check this fiddle http://sqlfiddle.com/#!2/40b8f/2

Consolidating results on one row SQL

I would like to consolidate a one to many relationship that outputs on different rows to a single row.
(select rate_value1
FROM xgenca_enquiry_event
INNER JOIN xgenca_enquiry_iso_code_translation
ON
xgenca_enquiry_event_rate.rate_code_id
= xgenca_enquiry_iso_code_translation.id
where xgenca_enquiry_event_rate.event_id = xgenca_enquiry_event.id
and ISO_code = 'PDIV') as PDIVrate,
(select rate_value1
FROM xgenca_enquiry_event
INNER JOIN xgenca_enquiry_iso_code_translation
ON
xgenca_enquiry_event_rate.rate_code_id
= xgenca_enquiry_iso_code_translation.id
where xgenca_enquiry_event_rate.event_id = xgenca_enquiry_event.id
and ISO_code = 'TAXR') as TAXrate
PDIVrate TAXrate
NULL 10.0000000
0.0059120 NULL
I would like the results on one row. Any help would be greatly appreciated.
Thanks.
You can use an aggregate function to perform this:
select
max(case when ISO_code = 'PDIV' then rate_value1 end) PDIVRate,
max(case when ISO_code = 'TAXR' then rate_value1 end) TAXRate
FROM xgenca_enquiry_event_rate r
INNER JOIN xgenca_enquiry_iso_code_translation t
ON r.rate_code_id = t.id
INNER JOIN xgenca_enquiry_event e
ON r.event_id = e.id
It looks like you are joining three tables are are identical in the queries. This consolidates this into a single query using joins.
Look here:
Can I Comma Delimit Multiple Rows Into One Column?
Simulate Oracle's LISTAGG() in SQL Server using STUFF:
SELECT Column1,
stuff((
SELECT ', ' + Column2
FROM tableName as t1
where t1.Column1 = t2.Column1
FOR XML PATH('')
), 1, 2, '')
FROM tableName as t2
GROUP BY Column1
/
Copied from here: https://github.com/jOOQ/jOOQ/issues/1277