I'm extracting some data from XML type of fields, getting some values from specific tags, and I managed to gather them in a form of such results:
echo | type| color | active
-------------------------------------------
echo1 | car | yellow,green,blue | no,no,no
echo1 | car | yellow,green | yes,yes
echo2 | car | green,blue,red | no,no,no
echo2 | car | blue,red | yes,yes
echo3 | car | yellow,green | no,yes
...
Now I need to convert those results into this:
echo | type| color | active
------------------------------
echo1 | car | yellow | no
echo1 | car | green | no
echo1 | car | blue | no
echo1 | car | yellow | yes
echo1 | car | green | yes
echo2 | car | green | no
echo2 | car | blue | no
echo2 | car | red | no
echo2 | car | blue | yes
echo2 | car | red | yes
echo3 | car | yellow | no
echo3 | car | green | yes
...
meaning, I need to split values from the 3rd and 4th columns at once. Each of the values from color column has a corresponding value in the active column and I need to match those pairs in records.
So having such results I use this:
SELECT echo, type, trim(x.COLUMN_VALUE) color, trim(y.COLUMN_VALUE) active
FROM
( RESULTS )
, xmltable(('"' || REPLACE(color, ',', '","') || '"')) x, xmltable(('"' || REPLACE(active, ',', '","') || '"')) y
ORDER BY echo, type, color, active
It works correctly if I convert only one of those two columns at once, but when I want to convert 2 at once, it multiplies the records, as if multiplying values from column y by values from column x.
If in the last column there were only "yes" or only "no" values, then it would work to use the distinct in the select, it would eliminate the obsolete (not true) records. But with mixed values I'm struggling to do it.
Can anyone give me a tip how to fix it to not get obsolete records?
Thanks in advance!
Sample XML:
<rowCollection>
<row>
<column name="active">YES</column>
<column name="customertype">Default</column>
<column name="type">Default</column>
<column name="risklevel">Default</column>
<column name="color">yellow</column>
</row>
<row>
<column name="active">YES</column>
<column name="customertype">Default</column>
<column name="type">Default</column>
<column name="risklevel">Default</column>
<column name="color">green</column>
</row>
</rowCollection>
Snippet of query used to generate results:
xmlcast(
xmlquery(
'string-join(/rowCollection/row/column[5]/text(),",")'
passing xml_field
RETURNING CONTENT
) AS VARCHAR2(500)
) color,
xmlcast(
xmlquery(
'string-join(/rowCollection/row/column[1]/text(),",")'
passing xml_field
RETURNING CONTENT
) AS VARCHAR2(300)
) active
Use a recursive sub-query factoring clause and use INSTR
to iteratively find the delimiters and then SUBSTR
to extract the values:
Oracle Setup :
CREATE TABLE results ( echo, type, color, active ) AS
SELECT 'echo1', 'car', 'yellow,green,blue', 'no,no,no' FROM DUAL UNION ALL
SELECT 'echo1', 'car', 'yellow,green', 'yes,yes' FROM DUAL UNION ALL
SELECT 'echo2', 'car', 'green,blue,red', 'no,no,no' FROM DUAL UNION ALL
SELECT 'echo2', 'car', 'blue,red', 'yes,yes' FROM DUAL UNION ALL
SELECT 'echo3', 'car', 'yellow,green', 'no,yes' FROM DUAL;
Query :
WITH lines ( echo, type, rn, idx, color, active, c_start, c_end, a_start, a_end ) AS (
SELECT echo,
type,
ROW_NUMBER() OVER ( ORDER BY echo, type, color, active ),
1,
color,
active,
1,
INSTR(color,',',1),
1,
INSTR(active,',',1)
FROM results
UNION ALL
SELECT echo,
type,
rn,
idx + 1,
color,
active,
c_end + 1,
INSTR(color,',',c_end + 1),
a_end + 1,
INSTR(active,',',a_end + 1)
FROM lines
WHERE c_end > 0
AND a_end > 0
)
SELECT echo,
type,
CASE c_end
WHEN 0
THEN SUBSTR( color, c_start )
ELSE SUBSTR( color, c_start, c_end - c_start )
END AS color,
CASE a_end
WHEN 0
THEN SUBSTR( active, a_start )
ELSE SUBSTR( active, a_start, a_end - a_start )
END AS active
FROM lines
ORDER BY rn, idx
Output :
\nECHO | TYPE | COLOR | ACTIVE\n:---- | :--- | :----- | :----- \necho1 | car | yellow | yes \necho1 | car | green | yes \necho1 | car | yellow | no \necho1 | car | green | no \necho1 | car | blue | no \necho2 | car | blue | yes \necho2 | car | red | yes \necho2 | car | green | no \necho2 | car | blue | no \necho2 | car | red | no \necho3 | car | yellow | no \necho3 | car | green | yes \n
db<>fiddle here
Update
Don't aggregate and then split the delimited strings; just extract the values from the XML:
CREATE TABLE table_name ( xml_field ) AS
SELECT XMLTYPE( '<rowCollection>
<row>
<column name="active">YES</column>
<column name="customertype">Default</column>
<column name="type">Default</column>
<column name="risklevel">Default</column>
<column name="color">yellow</column>
</row>
<row>
<column name="active">YES</column>
<column name="customertype">Default</column>
<column name="type">Default</column>
<column name="risklevel">Default</column>
<column name="color">green</column>
</row>
</rowCollection>' ) FROM DUAL;
Query :
SELECT x.*
FROM table_name t
CROSS JOIN
XMLTABLE(
'/rowCollection/row'
PASSING t.xml_field
COLUMNS
active VARCHAR2(3) PATH '/row/column[@name="active"]',
type VARCHAR2(10) PATH '/row/column[@name="type"]',
color VARCHAR2(10) PATH '/row/column[@name="color"]'
) x;
Output :
There will be a way to generate the echo
column; its just this isn't included in your sample data.
\nACTIVE | TYPE | COLOR \n:----- | :------ | :----- \nYES | Default | yellow\nYES | Default | green \n
db<>fiddle here
You can use hierarchy
query as following:
SQL> SELECT ECHO, TYPE, 2 REGEXP_SUBSTR(COLOR, '[^,]+', 1, COLUMN_VALUE) AS COLOR, 3 REGEXP_SUBSTR(ACTIVE, '[^,]+', 1, COLUMN_VALUE) AS ACTIVE 4 FROM RESULTS 5 CROSS JOIN TABLE ( CAST(MULTISET( 6 SELECT LEVEL FROM DUAL 7 CONNECT BY LEVEL <= REGEXP_COUNT(COLOR, ',') + 1 8 ) AS SYS.ODCINUMBERLIST) ); ECHO TYP COLOR ACTIVE ----- --- ---------- -------------------------------- echo1 car yellow no echo1 car green no echo1 car blue no echo1 car yellow yes echo1 car green yes echo2 car green no echo2 car blue no echo2 car red no echo2 car blue yes echo2 car red yes echo3 car yellow no echo3 car green yes 12 rows selected. SQL>
Cheers!!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.