XPath concat with EXTRACT function Oracle - sql

I want to extract the values of "KEY" and "VALUE" and concat these results using XPath with EXTRACT.
Sample XML as below.
<PivotSet>
<item>
<column name = "KEY">RET_1</column>
<column name = "VALUE">A</column>
</item>
<item>
<column name = "KEY">RET_2</column>
<column name = "VALUE">R</column>
</item>
<item>
<column name = "KEY">RET_3</column>
<column name = "VALUE">P</column>
</item>
</PivotSet>
I need to return in SQL something like
RET_1#A;RET_2#R;RET_3#P
where the delimiter '#' separates the key/value and ':' separates the items. I want to do it with EXTRACT and XPath.

You can use ListAgg along with an XmlTable, see below:
select LISTAGG(keyItem || '#' || keyValue, ';') WITHIN GROUP (ORDER BY keyItem) AS Concated
from XmlTable( '/PivotSet/item' PASSING XmlType('<PivotSet>
<item>
<column name = "KEY">RET_1</column>
<column name = "VALUE">A</column>
</item>
<item>
<column name = "KEY">RET_2</column>
<column name = "VALUE">R</column>
</item>
<item>
<column name = "KEY">RET_3</column>
<column name = "VALUE">P</column>
</item>
</PivotSet>') COLUMNS keyItem PATH '*:column[#name = "KEY"]',
keyValue PATH '*:column[#name = "VALUE"]');
You could use ExtractValue, but you would need to know how many //item elements are in your XML and then indicate in your path portion of the extract call, but this is a little messy IMO:
SELECT ExtractValue(xmlOut, '//item[1]/column[#name="KEY"]') || '#' || ExtractValue(xmlOut, '//item[1]/column[#name="VALUE"]') || ';' ||
ExtractValue(xmlOut, '//item[2]/column[#name="KEY"]') || '#' || ExtractValue(xmlOut, '//item[2]/column[#name="VALUE"]') || ';' ||
ExtractValue(xmlOut, '//item[3]/column[#name="KEY"]') || '#' || ExtractValue(xmlOut, '//item[3]/column[#name="VALUE"]') As Concatted
FROM
(
SELECT XmlType('<PivotSet>
<item>
<column name = "KEY">RET_1</column>
<column name = "VALUE">A</column>
</item>
<item>
<column name = "KEY">RET_2</column>
<column name = "VALUE">R</column>
</item>
<item>
<column name = "KEY">RET_3</column>
<column name = "VALUE">P</column>
</item>
</PivotSet>') as xmlOut FROM dual
);

Related

How to concate joined table fields for xml

Hi have data structure like this :
CREATE TEMP TABLE test_names (
id serial primary key,
name character varying(50),
age int
);
INSERT INTO test_names(name,age) values ('name1',10),('name2',20);
CREATE TEMP TABLE test_names_details (
id serial primary key,
test_names_id int,
col1 int,
col2 int,
col3 int
);
INSERT INTO test_names_details(test_names_id,col1,col2,col3)
VALUES(1,2,3,4),(1,5,6,7),(1,8,9,10),(2,20,21,22),(2,23,24,25)
Want from this tables select data like xml :
<info>
<maininfo>
<pn name="name">name1</pn>
<age name="age">10</age>
</maininfo>
<data>
<row>
<col1 name="col1">2</col1>
<col2 name="col2">3</col1>
<col3 name="col3">4</col1>
</row>
<row>
<col1 name="col1">5</col1>
<col2 name="col2">6</col1>
<col3 name="col3">7</col1>
</row>
<row>
<col1 name="col1">8</col1>
<col2 name="col2">9</col1>
<col3 name="col3">10</col1>
</row>
</data>
<maininfo>
<pn name="name">name2</pn>
<age name="age">20</age>
</maininfo>
<data>
<row>
<col1 name="col1">20</col1>
<col2 name="col2">21</col1>
<col3 name="col3">22</col1>
</row>
<row>
<col1 name="col1">23</col1>
<col2 name="col2">24</col1>
<col3 name="col3">25</col1>
</row>
</data>
</info>
How to do it ?
Wrap this in a function and off you go! ;-)
SELECT
'<info>' ||
string_agg(
'<maininfo>' ||
'<pn name="name">' || name || '</pn>' ||
'<age name="age">' || age || '</age>' ||
'</maininfo>' ||
'<data>' ||
(SELECT
string_agg(
'<row>' ||
'<col name="col1">'||col1||'</col>' ||
'<col name="col2">'||col2||'</col>' ||
'<col name="col3">'||col3||'</col>' ||
'</row>', '')
FROM test_names_details WHERE test_names_id = test_names.id
) ||
'</data>'
, '') ||
'</info>'
FROM test_names

How to insert XML to SQL

I try to add a XML file to SQL 2008.
My XML:
<ItemList>
<Section Index="0" Name="cat0">
<Item Index="0" Slot="0" />
<Item Index="1" Slot="0" />
</Section>
<Section Index="1" Name="cat1">
<Item Index="33" Slot="0" />
<Item Index="54" Slot="0" />
</Section>
<Section Index="2" Name="cat2">
<Item Index="55" Slot="0" />
<Item Index="78" Slot="0" />
</Section>
</ItemList>
SQL Column :
Name = Section Name,
Cat = Section Index,
Index = Item Index,
Slot = Item Slot.
My Example :
DECLARE #input XML = 'MY XML file'
SELECT
Name = XCol.value('#Index','varchar(25)'),
Cat = XCol.value('#Name','varchar(25)'),
[Index] = 'Unknown', /* Index from <Item>*/
Slot = 'Unknown' /* Slot from <Item> */
FROM #input.nodes('/ItemList/Section') AS test(XCol)
I don't know how to add values from "Item".
Thank you very much!
You can do it like this:
select
Name = XCol.value('../#Index','varchar(25)'),
Cat = XCol.value('../#Name','varchar(25)'),
[Index] = XCol.value('#Index','varchar(25)'),
Slot = XCol.value('#Slot','varchar(25)')
from
#input.nodes('/ItemList/Section/Item') AS test(XCol)
Key idea: take data one level deeper, not /ItemList/Section, but /ItemList/Section/Item. So in this case you are able to access attributes of Item and also you can access attributes of parent element (Section in your case) by specifying ../#Attribute_Name
Different than the previous answer - CROSS APPLY with the children Item nodes:
SELECT
Name = XCol.value('#Index','varchar(25)'),
Cat = XCol.value('#Name','varchar(25)'),
[Index] = XCol2.value('#Index','varchar(25)'),
Slot = XCol2.value('#Slot','varchar(25)')
FROM #input.nodes('/ItemList/Section') AS test(XCol)
CROSS APPLY XCol.nodes('Item') AS test2(XCol2)

How can I create a txt file in xml using Sql server?

How can i create the below xml file from sql server?
<Database>
<Tables>
<Table Name="Student"> <Files> <File FileName="Student_Info.txt" NumberOfRows="44" /> </Files>
<Columns>
<Column Name="Name" DataType="nvarchar" Length="50" />
<Column Name="Department" DataType="nvarchar" Length="30" />
<Column Name="Phone" DataType="nvarchar" Length="20" />
</Columns> </Table>
<Table Name="Teacher">
<Files>
<File FileName="Teacher.txt" NumberOfRows="33" />
</Files>
<Columns>
<Column Name="TID" DataType="ANSI INTEGER" IsPrimaryKey="true" />
<Column Name="TName" DataType="ANSI CHARACTER VARYING" Length="50" />
</Columns>
</Table>
</Database>
Here I want to make a database where all table data will save in a txt file and the Number of rows will change everytime.
This query would give you the current database format output like what you have
select
(
select TABLE_NAME as '#Name' ,
( select COLUMN_NAME as '#Name',
Data_type as '#DataType',
character_octet_length as '#Length'
FROM information_schema.COLUMNS C
WHERE IT.TABLE_NAME = C.TABLE_NAME
FOR XML PATH ('Column'), TYPE
),
(select
( SELECT distinct
d.physical_name as '#FileName',
i.rowcnt as '#NumberofRows'
FROM sys.sysindexes i,
sys.filegroups f,
sys.database_files d,
sys.data_spaces s
WHERE OBJECTPROPERTY(i.id, 'IsUserTable') = 1
AND f.data_space_id = i.groupid
AND f.data_space_id = d.data_space_id
AND f.data_space_id = s.data_space_id
AND IT.TABLE_NAME = OBJECT_NAME(i.id)
FOR XML path ('File'), Type
)
FOR XML PATH ('Files'), Type
)
from INFORMATION_SCHEMA.TABLES IT
for xml path ('Table'), TYPE
)
for xml path ('Databases')

How to do multiple loops through XML in PL/SQL

My XML looks like this
<data>
<row>
<id>1</id>
<name>John</name>
<name>Jack</name>
</row>
<row>
<id>2</id>
<name>Scott</name>
<name>Chuck</name>
<name>Kim</name>
</row>
</data>
I would like output:
->1
-->John
-->Jack
->2
-->Scott
-->Chuck
-->Kim
My current code looks like this:
DECLARE
X XMLTYPE := XMLTYPE('<?xml version="1.0" ?>
<data>
<row>
<id>1</id>
<name>John</name>
<name>Jack</name>
</row>
<row>
<id>2</id>
<name>Scott</name>
<name>Chuck</name>
<name>Kim</name>
</row>
</data>');
BEGIN
FOR R IN (SELECT EXTRACTVALUE(VALUE(P), '/row/id/text()') AS NAME
FROM TABLE(XMLSEQUENCE(EXTRACT(X, '//data/row'))) P)
LOOP
DBMS_OUTPUT.PUT_LINE('-->' || R.NAME);
END LOOP;
END;
I would need one more loop inside a row to loop through name tag, but I don't know how to do it.
A little help would be appreciated.
I figured it by myself:
DECLARE
X XMLTYPE := XMLTYPE('<?xml version="1.0" ?>
<data>
<row>
<id>1</id>
<promet>
<name>John</name>
<name>Jack</name>
</promet>
</row>
<row>
<id>2</id>
<promet>
<name>Scott</name>
<name>Chuck</name>
<name>Kim</name>
</promet>
</row>
</data>');
BEGIN
FOR R IN (SELECT EXTRACTVALUE(VALUE(P), '/row/id/text()') AS ID,
EXTRACT(VALUE(P), '/row/promet') AS PROMET
FROM TABLE(XMLSEQUENCE(EXTRACT(X, '//data/row'))) P)
LOOP
DBMS_OUTPUT.PUT_LINE('-->' || R.ID);
FOR R1 IN (SELECT EXTRACTVALUE(VALUE(T1), '/name/text()') AS NAME
FROM TABLE(XMLSEQUENCE(EXTRACT(R.PROMET, 'promet/name'))) T1)
LOOP
DBMS_OUTPUT.PUT_LINE('-->' || R1.NAME);
END LOOP;
END LOOP;
END;

Extracting part of XMLType in PL/SQL because of repeating node inside another repeating mode

I have a XMLType object and I want to extract opening times into table.
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
<workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
<workspace>
I would use something like:
SELECT ExtractValue(Value(p),'workspace/item/day/text()') as day
,ExtractValue(Value(p),'workspace/item/openingTime/text()') as open
,ExtractValue(Value(p),'workspace/item/closingTime/text()') as close
FROM TABLE (XMLSequence(Extract(y,'workspace'))) p
WHERE ExtractValue(Value(p),'/workspace/title/text()') LIKE 'workspace1';
where y is XMLType above. But that won't work, because it will still find more than one item node. I need to extract ALL element values for title workspace2 (values 1, 9:00, 14:00, 3, 12:00, 16:00). It would help if I could extract not only value, but whole part of XMLType. Any ideas?
Thanks, Michal
Your target may be achieved by using XMLTable :
with x as ( -- Just to introduce XML parameter
select
xmltype('
<workspace_list>
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
</workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
</workspace>
</workspace_list>
') xfield
from dual
)
select
workspace, day, opening_time, closing_time
from
XMLTable(
'
for $i in $doc//workspace[title eq $workspace_filter]
for $j in $i/item
return
<wks_item>
<wks_name>{$i/title/text()}</wks_name>
{$j/*}
</wks_item>
'
passing
(select xfield from x) as "doc",
('workspace1') as "workspace_filter"
columns
workspace varchar2(100) path '//wks_name',
day varchar2(100) path '//day',
opening_time varchar2(100) path '//openingTime',
closing_time varchar2(100) path '//closingTime'
)
SQLFiddle Example
Note that I introduced <workspace_list> top element and added slash for closing <workspace> elements to make XML valid.
With some #ThinkJet tricks your query may look like this
with x as (
select
xmltype('
<workplaces>
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
</workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
</workspace>
</workplaces>
') xfield
from dual
)
SELECT "day", "openingTime", "closingTime"
FROM xmltable('$doc//workspace[title=$workspace_filter]/item'
passing (select xfield from x) as "doc",
('workspace1') as "workspace_filter"
columns "openingTime" path '//openingTime',
"closingTime" path '//closingTime',
"day" path '//day')