Reputation: 37
I'm using Oracle SQL. This is the table:
doc-nr | code | text |
---|---|---|
0000010 | 155 | Sample text from code 155 |
0000010 | 455 | Sample text1 from code 455 |
0000010 | 455 | Sample text2 from code 455 |
0000010 | 455 | Sample text3 from code 455 |
With this query:
SELECT t155.text as "155", t455.text as "455"
FROM myTable t155
JOIN myTable t455 ON t155.doc-nr = t455.doc-nr
WHERE t155.code = '155'
AND t455.code = '455'
I get ugly result:
155 | 455 |
---|---|
Sample text from code 155 | Sample text1 from code 455 |
Sample text from code 155 | Sample text2 from code 455 |
Sample text from code 155 | Sample text3 from code 455 |
I want the output to be one row:
155 | 455 | 455 | 455 |
---|---|---|---|
Sample text from code 155 | Sample text1 from code 455 | Sample text2 from code 455 | Sample text3 from code 455 |
Or:
155 | 455 |
---|---|
Sample text from code 155 | Sample text1 from code 455; Sample text2 from code 455; Sample text3 from code 455 |
How can you do this with Oracle SQL without using PIVOT or LISTAGG or using custom built functions?
Upvotes: 1
Views: 240
Reputation: 2212
Using xml functions in a such way is not good for performance (when there is more data) but in this case it works fine:
SELECT t155.text as "155",
sys_xmlagg(xmlelement(col, t455.text || ',')).extract('/ROWSET/COL/text()')
.getclobval() as "455"
FROM myTable t155
JOIN myTable t455
ON t155."doc-nr" = t455."doc-nr"
WHERE t155.code = '155'
AND t455.code = '455'
GROUP BY t155.text
Upvotes: 0
Reputation: 65313
You can create such a function returning SYS_REFCURSOR
in order to pivot your data dynamically
CREATE OR REPLACE FUNCTION Get_Pivoted_Cols RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols1 VARCHAR2(32767);
v_cols2 VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||rn||''' AS "'||rn||'"' , ',' ) WITHIN GROUP ( ORDER BY text ),
LISTAGG( 'MAX("'||rn||'") AS "'||rn||'"' , ',' ) WITHIN GROUP ( ORDER BY text )
INTO v_cols1, v_cols2
FROM
( SELECT code||'_'||ROW_NUMBER() OVER (PARTITION BY doc_nr, code ORDER BY text) AS rn, t.*
FROM tab t );
v_sql :='SELECT '||v_cols2||
' FROM
(
SELECT code||''_''||ROW_NUMBER() OVER (PARTITION BY doc_nr, code ORDER BY text) AS rn, t.*
FROM tab t
)
PIVOT
(
MAX(text) FOR rn IN ( '|| v_cols1 ||' )
)
GROUP BY doc_nr';
OPEN v_recordset FOR v_sql;
DBMS_OUTPUT.PUT_LINE(v_sql);
RETURN v_recordset;
END;
which will generate this output as a SQL statement
SELECT MAX("155_1") AS "155_1",
MAX("455_1") AS "455_1",
MAX("455_2") AS "455_2",
MAX("455_3") AS "455_3"
FROM
(
SELECT code||'_'||ROW_NUMBER() OVER (PARTITION BY doc_nr, code ORDER BY text) AS rn,
t.*
FROM tab t
)
PIVOT
(
MAX(text) FOR rn IN ( '155_1' AS "155_1", '455_1' AS "455_1", '455_2' AS "455_2",
'455_3' AS "455_3" )
)
GROUP BY doc_nr;
and then run this code :
VAR rc REFCURSOR
EXEC :rc := Get_Pivoted_Cols;
PRINT rc
from SQL Developer's Command Line in order to see the result set.
Upvotes: 2
Reputation: 191285
If the problem is the size, you can use XML aggregation:
select mt.doc_nr, mt.text as text_155,
xmlquery('
for $i in .
return <e>{ fn:string-join($i/text, "; ") }</e>/text()'
passing tmp.xml
returning content
).getclobval() as text_455
from mytable mt
join (
select doc_nr, xmlagg(xmlelement("text", text)) as xml
from mytable
where code = 455
group by doc_nr
) tmp
on tmp.doc_nr = mt.doc_nr
where mt.code = 155
DOC_NR | TEXT_155 | TEXT_455
:------ | :------------------------ | :---------------------------------------------------------------------------------
0000010 | Sample text from code 155 | Sample text1 from code 455; Sample text3 from code 455; Sample text2 from code 455
db<>fiddle including the intermediate steps.
Upvotes: 1