Reputation: 2447
I have an existing table like :
CREATE TABLE ES_DEVISES (
NOM VARCHAR2(500),
DEVISE VARCHAR2(5),
ORDRES NUMBER(5,0)
)
-- For testing purposes :
INSERT INTO ES_DEVISES VALUES ('ES1','CHF',157);
INSERT INTO ES_DEVISES VALUES ('ES1','USD',1328);
INSERT INTO ES_DEVISES VALUES ('ES2','AUD',5);
INSERT INTO ES_DEVISES VALUES ('ES1','AUD',23);
INSERT INTO ES_DEVISES VALUES ('ES1','CNY',17);
INSERT INTO ES_DEVISES VALUES ('ES1','INR',17);
INSERT INTO ES_DEVISES VALUES ('ES2','CNY',1);
INSERT INTO ES_DEVISES VALUES ('ES2','INR',4);
INSERT INTO ES_DEVISES VALUES ('ES2','USD',218);
INSERT INTO ES_DEVISES VALUES ('ES2','CHF',42);
And I have that pivots rows to columns like this, that returns the desired output :
SELECT * FROM ES_DEVISES
PIVOT (
MAX(ORDRES) FOR DEVISE IN ('USD' USD,'CHF' CHF,'CNY' CNY,'INR' INR,'AUD' AUD)
);
-- Output :
NOM USD CHF CNY INR AUD
---------------- ---------- ---------- ---------- ---------- ----------
ES1 1328 157 17 17 23
ES2 218 42 1 4 5
Now, with some evolution the table ES_DEVISES
will contain random units (ES1, ES2, ES3, ...) and random currencies (USD, EUR, XRP, BTC, ...) so the query above won't be valid anymore. I found this answer that recommends using PIVOT XML
so the query became :
SELECT NOM, DEVISE_XML
FROM ES_DEVISES
PIVOT XML(
MAX(ORDRES) FOR DEVISE IN (SELECT DEVISE FROM NEW_TABLE_FOR_CURRENCIES)
) t;
-- Output :
NOM DEVISE_XML
---------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ES1 <PivotSet><item><column name = "DEVISE">AUD</column><column name = "MAX(ORDRES)">23</column></item><item><column name = "DEVISE">CHF</column><column name = "MAX(ORDRES)">157</column></item><item><column name = "DEVISE">CNY</column><column name = "MAX(ORDRES)">17</column></item><item><column name = "DEVISE">INR</column><column name = "MAX(ORDRES)">17</column></item><item><column name = "DEVISE">USD</column><column name = "MAX(ORDRES)">1328</column></item></PivotSet>
ES2 <PivotSet><item><column name = "DEVISE">AUD</column><column name = "MAX(ORDRES)">5</column></item><item><column name = "DEVISE">CHF</column><column name = "MAX(ORDRES)">42</column></item><item><column name = "DEVISE">CNY</column><column name = "MAX(ORDRES)">1</column></item><item><column name = "DEVISE">INR</column><column name = "MAX(ORDRES)">4</column></item><item><column name = "DEVISE">USD</column><column name = "MAX(ORDRES)">218</column></item></PivotSet>
The next step now is to parse that XML so I can get back to a result set that looks like the first output. The problem now is that the DEVISE_XML
field doesn't contain the NOM
field and I couldn't find a way to parse the XML to dynamic columns, I tried to use XMLTABLE
but it gets me back to something like SELECT * FROM ES_DEVISES
.
My question is : is there any way to either :
1) return an XML looking like this :
<PivotSet>
<row>
<unit>ES1</unit>
<column name="AUD">23</column>
<column name="CHF">157</column>
<column name="CNY">17</column>
<column name="INR">17</column>
<column name="USD">1328</column>
</row>
<row>
<unit>ES2</unit>
<column name="AUD">218</column>
<column name="CHF">42</column>
<column name="CNY">1</column>
<column name="INR">4</column>
<column name="USD">5</column>
</row>
</PivotSet>
2) Parse the XML to return the desired output (which should look like the output from the first query) dynamically.
PS : I saw many solutions in the net like using LISTAGG
or building the query with a string and executing it with EXECUTE IMMEDIATE
. But none of that interest me because I could just easily do it with JAVA but the aim of this question is to find a clear, easily maintainable query that's doing all the job from a PROCEDURE
.
Upvotes: 1
Views: 844
Reputation: 35920
I am using dynamic PIVOT function for the such requirement in my projects.
I have added one more row, which is random. -- As per your requirement.
CREATE TABLE ES_DEVISES (
NOM VARCHAR2(500),
DEVISE VARCHAR2(5),
ORDRES NUMBER(5,0)
);
-- For testing purposes :
INSERT INTO ES_DEVISES VALUES ('ES1','CHF',157);
INSERT INTO ES_DEVISES VALUES ('ES1','USD',1328);
INSERT INTO ES_DEVISES VALUES ('ES2','AUD',5);
INSERT INTO ES_DEVISES VALUES ('ES1','AUD',23);
INSERT INTO ES_DEVISES VALUES ('ES1','CNY',17);
INSERT INTO ES_DEVISES VALUES ('ES1','INR',17);
INSERT INTO ES_DEVISES VALUES ('ES2','CNY',1);
INSERT INTO ES_DEVISES VALUES ('ES2','INR',4);
INSERT INTO ES_DEVISES VALUES ('ES2','USD',218);
INSERT INTO ES_DEVISES VALUES ('ES2','CHF',42);
INSERT INTO ES_DEVISES VALUES ('ES3','RNDM',100); -- random record added by me
SELECT
*
FROM
TABLE ( PIVOT(' select NOM
, DEVISE
, MAX(ORDRES) ORDRES
from ES_DEVISES
group
by NOM
, DEVISE
'
) );
OUTPUT
Here, PIVOT is the function and I got it from AMIS.
Here is the link to download the function: Dynamic PIVOT
Extract the downloaded zip and execute the script pivotFun.sql to create the PIVOT function in your DB.
Hope, You will like it.
Upvotes: 1