Delete Empty tag from xmltype oracle - sql

i want try to delete the empty tag from xmltype. I Have generate the below xml from oracle type. In the collection few elements does not have values so i generated with empty tag.
Can any one please help me out:
Actual output:
<MESSAGE>
<LOCATIONS>
<LOCATION_ID>9999</LOCATION_ID>
<LOC_TYPE>S</LOC_TYPE>
<NAME>Test Location</NAME>
<PHONE_NUM>08 </PHONE_NUM>
<LAST_MODIFIED_BY/>
<LAST_MODIFIED_DATE/>
<POS_CODE/>
</LOCATIONS>
</MESSAGE>
Expected output:
<MESSAGE>
<LOCATIONS>
<LOCATION_ID>9999</LOCATION_ID>
<LOC_TYPE>S</LOC_TYPE>
<NAME>Test Location</NAME>
<PHONE_NUM>08 </PHONE_NUM>
</LOCATIONS>
</MESSAGE>

Use DELETEXML and look for the XPath //*[not(text())][not(*)] to find elements that contain no text and no children:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( xml ) AS
SELECT XMLTYPE( '<MESSAGE>
<LOCATIONS>
<LOCATION_ID>9999</LOCATION_ID>
<LOC_TYPE>S</LOC_TYPE>
<NAME>Test Location</NAME>
<PHONE_NUM>08 </PHONE_NUM>
<LAST_MODIFIED_BY/>
<LAST_MODIFIED_DATE/>
<POS_CODE/>
</LOCATIONS>
</MESSAGE>' ) FROM DUAL;
Query 1:
SELECT DELETEXML(
xml,
'//*[not(text())][not(*)]'
).getStringVal()
FROM table_name
Results:
| DELETEXML(XML,'//*[NOT(TEXT())][NOT(*)]').GETSTRINGVAL() |
|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| <MESSAGE><LOCATIONS><LOCATION_ID>9999</LOCATION_ID><LOC_TYPE>S</LOC_TYPE><NAME>Test Location</NAME><PHONE_NUM>08 </PHONE_NUM></LOCATIONS></MESSAGE> |

SELECT
deletexml(xml_data, '//*[not(text())][not(*)]').getstringval()
FROM
(
SELECT
xmltype('<MESSAGE>
<LOCATIONS>
<LOCATION_ID>9999</LOCATION_ID>
<LOC_TYPE>S</LOC_TYPE>
<NAME>Test Location</NAME>
<PHONE_NUM>08 </PHONE_NUM>
<LAST_MODIFIED_BY/>
<LAST_MODIFIED_DATE/>
<POS_CODE/>
</LOCATIONS>
</MESSAGE>'
) xml_data
FROM
dual
)
this is working fine thanks

Related

I want to get the condition for each tag and have table like this in sql

how can get the data from different tag of xml in sql
output
tag condition
_________________ ____________
GroupHeaderBand1 nature
GroupHeaderBand2 job
GroupHeaderBand3 name
input:
<StiSerializer version="1.02" type="Net" application="StiReport">
<Pages isList="true" count="1">
<Page1 Ref="3" type="Page" isKey="true">
<Components isList="true" count="12">
<GroupHeaderBand1 Ref="18" type="GroupHeaderBand" isKey="true">
<Condition>Nature</Condition>
</GroupHeaderBand1>
<GroupHeaderBand2 Ref="21" type="GroupHeaderBand" isKey="true">
<Condition>Job</Condition>
</GroupHeaderBand2>
<GroupHeaderBand3 Ref="26" type="GroupHeaderBand" isKey="true">
<Condition>Name</Condition>
</GroupHeaderBand3>
</Components>
</Page1>
this is my xml code
use following query
SELECT
A.evnt.value('local-name(.)', 'varchar(100)') AS tag,
A.evnt.value('(.)[1]','varchar(100)') as condition
FROM #XML.nodes('StiSerializer/Pages/Page1/Components/*') A(evnt)
or use
SELECT
A.evnt.value('local-name(.)', 'varchar(100)') AS tag,
A.evnt.value('(Condition/text())[1]','varchar(100)') as condition
FROM #XML.nodes('StiSerializer/Pages/Page1/Components/*') A(evnt)
demo in db<>fiddle

Extracting multiple values from BLOB as XML

I have an XML like this in a BLOB column:
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>
As you can see the year can be either in the event level or at country level. There can be multiple events and multiple countries per event. This whole XML is stored in a BLOB column in Oracle. I need to extract the value of the year or better check if the year is 2000 and if so return the primary key of the row.
I used EXISTSNODE to check if the year tag is present.
select pk from table where XMLType(blobdata, nls_charset_id('UTF8')).EXISTSNODE('/Document/history/Event[*]/year',
'xmlns="urn:xyzns"') = 1 and EXTRACTVALUE(XMLTYPE(blobdata, nls_charset_id('UTF8')), '/Document/history/Event[*]/year/text()',
'xmlns="urn:xyzns"') = '2000';
However this fails and the extractvalue query returns multiple nodes, so I changed the parameter to '/Document/history/Event[1]/year/text()' to check and it works. However this wouldnt be enough as it only checks the first event tag.
I looked at other questions here and one of the options was to use XMLTABLE since extractvalue is deprecated. I am having trouble understanding the parameters given inside the XMLTABLE. Could someone explain how to use XMLTABLE in this scenario? I should point out that the original datatype is BLOB and not CLOB. Thank you.
Use XMLTABLE to get values for both locations and then use COALESCE to show whichever is not NULL:
SELECT COALESCE( year, country_year ) AS year,
location,
type
FROM table_name t
CROSS APPLY XMLTABLE(
XMLNAMESPACES( DEFAULT 'urn:xyzns' ),
'/document/history/Event'
PASSING XMLTYPE(t.blobdata, nls_charset_id('UTF8'))
COLUMNS
year NUMBER(4,0) PATH './year',
country_year NUMBER(4,0) PATH './Country/year',
location VARCHAR2(200) PATH './Country/Location',
type VARCHAR2(200) PATH './Country/Type'
) x
Which, for the sample data:
CREATE TABLE table_name ( blobdata BLOB );
INSERT INTO table_name
VALUES (
UTL_RAW.CAST_TO_RAW(
'<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>'
)
);
Outputs:
YEAR | LOCATION | TYPE
---: | :------- | :------------
1983 | Lisbon | Political
2016 | USA | Entertainment
db<>fiddle here

Parsing XML in SQL without a namespace

The code will be self explanatory to the right person for this, but any questions please shout...
Thanks,
Dave
DECLARE #XML XML
SET #XML =
'<?xml version="1.0" encoding="utf-8"?>
<updates>
<versions>
<installer type="A" Xversion="101" iniSizeInBytes="22480" dataSizeInBytes="23396349" msiSizeInBytes="4732928" />
<installer type="B" Yversion="201" iniSizeInBytes="22480" dataSizeInBytes="116687353" msiSizeInBytes="5807616" webconfigModifierSizeInBytes="11800" />
<installer type="A" Xversion="102" iniSizeInBytes="22480" dataSizeInBytes="23396349" msiSizeInBytes="4732928" />
<installer type="B" Yversion="202" iniSizeInBytes="22480" dataSizeInBytes="116687353" msiSizeInBytes="5807616" webconfigModifierSizeInBytes="11800" />
</versions>
<update setNumber="1" XVersion="101" YVersion="201">
<detail Ref="1000">some detail info for 101 and 201</detail>
</update>
<update setNumber="2" XVersion="102" YVersion="202">
<detail Ref="1001">some detail info for 102 and 202</detail>
</update>
</updates>
'
SELECT
r.value('#ref','NVARCHAR(250)') as 'Ref', --This is wrong, but you can probably see i'm wanting the value of Ref, eg 1000 for line 1, 1001 for line 2
t.r.query('./detail').value('.','nvarchar(max)') as 'Detail'
FROM #XML.nodes('/updates/update') AS t(r);
You need detail element in value method to get the value in Ref attribute
SELECT
r.value('#Ref','NVARCHAR(250)') as 'Ref', -- It should be #Ref instead of #ref
r.query('.').value('.','nvarchar(max)') as 'Detail'
FROM #XML.nodes('/updates/update/detail') AS t(r);
Note : Elements and attributes in xml is case sensitive you cannot use #ref in query when xml attribute is Ref
Rextester Demo

Export sql query results to xml data file in oracle

I have an SQL query which returns two columns with data like this:.
State Name
------- ---------
Online Terminal1
Offline Terminal2
Online Terminal3
Online Terminal4
Now I want to create an XML file with a SQL query runs. XML file structure must be like this:
<?xml version="1.0" encoding="utf-8" ?>
<Terminallist name="xml data">
<Value id="0">
<Terminal>Terminal1</Terminal>
<State>Online</State>
</Value>
<Value id="1">
<Terminal>Terminal2</Terminal>
<State>Offline</State>
</Value>
<Value id="2">
<Terminal>Terminal3</Terminal>
<State>Online</State>
</Value>
<Value id="3">
<Terminal>Terminal4</Terminal>
<State>Online</State>
</Value>
</Terminallist>
I want to save XML file to a directory like this c:/file.xml.
Answer:-
Table name: temptable
Data in table :
Query :-
SELECT XMLElement("Terminallist ", XMLAttributes('xml data' AS "name"),XMLAgg(XMLElement("value ", XMLAttributes(rownum AS "id"),XMLForest(Terminal,state))))
FROM temptable ;
output :-
<Terminallist name = "xml data">
<value id = "1">
<TERMINAL>Terminal2</TERMINAL>
<STATE>Offline</STATE>
</value>
<value id = "2">
<TERMINAL>Terminal3</TERMINAL>
<STATE>Online</STATE>
</value>
<value id = "3">
<TERMINAL>Terminal4</TERMINAL>
<STATE>Online</STATE>
</value>
</Terminallist>
Thanks
Narendar
This solution uses a WITH clause to generate the ID as you want, starting from 0. Using the analytic row_number() function provides a guaranteed sort order in the result set.
NB: XMLRoot() is deprecated as part of the XML/SQL standard but generates the version header you asked for. Find out more.
with cte as (
select row_number() over (order by name) - 1 as id
, name
, state
from terminals
)
SELECT xmlroot (
XMLElement(
"Terminallist",
XMLAttributes( 'xml data' as "name"),
XMLAgg(XMLElement("Value",
XMLAttributes(cte.id as "id"),
XMLElement("Terminal",cte.name),
XMLElement("State",cte.state)
)
)
)
, version '1.0' )
FROM cte
order by cte.id
/
Here is the output:
<?xml version="1.0"?>
<Terminallist name="xml data">
<Value id="0">
<Terminal>Terminal1</Terminal>
<State>Online</State>
</Value>
<Value id="1">
<Terminal>Terminal2</Terminal>
<State>Offline</State>
</Value>
<Value id="2">
<Terminal>Terminal3</Terminal>
<State>Online</State>
</Value>
<Value id="3">
<Terminal>Terminal4</Terminal>
<State>Online</State>
</Value>
</Terminallist>
As for writing the output to a file, that depends on how you want to call the SQL. Use SPOOL if you're running it from SQL*Plus and want to save it to a local file. If running from PL/SQL you'll need to use UTL_FILE and write to a directory on the database server.
Using the Oracle XE database as source data I'm able to export the employees table as XML as follows...
SELECT SYS_XMLAGG(
SYS_XMLGEN(
XMLFOREST(employee_id, first_name, last_name, email, phone, hire_date, manager_id, job_title)
)
) FROM employees;
The XML will look like...
<?xml version="1.0"?>
<ROWSET>
<ROW>
<EMPLOYEE_ID>107</EMPLOYEE_ID>
<FIRST_NAME>Summer</FIRST_NAME>
<LAST_NAME>Payne</LAST_NAME>
<EMAIL>summer.payne#example.com</EMAIL>
<PHONE>515.123.8181</PHONE>
<HIRE_DATE>2016-06-07</HIRE_DATE>
<MANAGER_ID>106</MANAGER_ID>
<JOB_TITLE>Public Accountant</JOB_TITLE>
</ROW>
<ROW>
<EMPLOYEE_ID>...
The CSV list of column names for XMLForest can be obtained thus...
SELECT LISTAGG(column_name, ',') FROM user_tab_columns WHERE table_name = 'EMPLOYEES';
Luck.

Save XML with attribute to Table in SQL Server

Hi I have XML data with attribute as input for SQL, i need this to be inserted in my table.
XML Data is
<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>
I want this to be inserted in below Format
GUID SUBMIT DATE ID ERROR SEQ CODE
kfafb3 2015-10-15 11:30:29 1 1 28681
kfafb3 2015-10-15 11:30:29 1 1 2868
please help.
Look into XPath and xml Data Type Methods in MSDN. This is one possible way :
declare #xml As XML = '...you XML string here...'
INSERT INTO YourTable
SELECT
guid.value('#GUID', 'varchar(100)') as 'GUID'
,guid.value('#SUBMITDATE', 'datetime') as 'SUBMIT DATE'
,guid.value('#ID', 'int') as 'ID'
,guid.value('ERROR[1]/#SEQ', 'int') as 'SEQ'
,guid.value('ERROR[1]/#CODE', 'int') as 'CODE'
FROM #xml.nodes('/MESSAGEACK/GUID') as x(guid)
Result :
just paste this into an empty query window and execute. Adapt to your needs:
DECLARE #xml XML=
'<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>';
SELECT Msg.Node.value('#GUID','varchar(max)') AS [GUID] --The value is no GUID, if the original values are, you could use uniqueidentifier instead of varchar(max)
,Msg.Node.value('#SUBMITDATE','datetime') AS SUBMITDATE
,Msg.Node.value('#ID','int') AS ID
,Msg.Node.value('(ERROR/#SEQ)[1]','int') AS [ERROR SEQ]
,Msg.Node.value('(ERROR/#CODE)[1]','int') AS CODE
FROM #xml.nodes('/MESSAGEACK/GUID') AS Msg(Node)