SQL FOR XML multilevel from one pivoted table - sql

I've been trying to use FOR XML without success to do the following.
Source table:
Country | ID | 1950 | 1955
-----------------------------------------------------
Country 1 | 1 | 2.43 | 2.55
Country 2 | 2 | 4.54 | 42.15
Desired output:
<locations>
<location>
<loc name='Country 1' id='1' />
<dub>
<data year='1950' value='2.43' />
<data year='1955' value='2.55' />
</dub>
</location>
<location>
<loc name='Country 2' id='2' />
<dub>
<data year='1950' value='4.54' />
<data year='1955' value='42.15' />
</dub>
</location>
</locations>
Will it be necessary to unpivot for the dub element? I wanted the simplest SQL query possible.
I think FOR XML is too difficult to use. You should be able to specify the hierarchy just using simple XPath on column names but it won't accept, for example, [dub/data/#year=1955/#value] as the name of the column [1950].

SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table YourTable
(
Country varchar(20),
ID int,
[1950] numeric(5,2),
[1955] numeric(5,2)
)
insert into YourTable values
('Country 1', 1, 2.43, 2.55),
('Country 2', 2, 4.54, 42.15)
Query 1:
select T.Country as 'loc/#name',
T.ID as 'loc/#id',
(
select 1950 as 'data/#year',
T.[1950] as 'data/#value',
null,
1955 as 'data/#year',
T.[1955] as 'data/#value'
for xml path(''), type
) as dub
from YourTable as T
for xml path('location'), root('locations'), type
Results:
<locations>
<location>
<loc name="Country 1" id="1" />
<dub>
<data year="1950" value="2.43" />
<data year="1955" value="2.55" />
</dub>
</location>
<location>
<loc name="Country 2" id="2" />
<dub>
<data year="1950" value="4.54" />
<data year="1955" value="42.15" />
</dub>
</location>
</locations>

Related

How to return multiple values from XML element in SQL?

I need to pull information from the "Name" element from an XML column in SQL. An example of the XML is below:
<ArrayOfTarget xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/TriTech.InformRMS.Domain.Core.ComplexTypes">
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true" />
<Id>19bc33e1-a788-cd99-3dab-08d92aa7d030</Id>
<Name>Case Number WB21-006637</Name>
<Type>Case Management</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true" />
<Id>cb4d829b-c31a-cadb-5c9e-08d934b7404d</Id>
<Name>Incident Supplement Number WB21-006637.006</Name>
<Type>Data Entry</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true" />
<Id>6b23195c-4650-c0c9-925d-08d71a88f611</Id>
<Name i:nil="true" />
<Type>Template</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true" />
<Id>b465517c-5926-c6b3-1cc6-08d6fbe6da27</Id>
<Name>Default Workflow</Name>
<Type>Workflow</Type>
</Target>
<Target>
<AgencyId i:nil="true" />
<AgencyName i:nil="true" />
<Id i:nil="true" />
<Name>Complete</Name>
<Type>Workflow Step</Type>
</Target>
</ArrayOfTarget>
I have this SQL Query which works for returning five of the "Name" elements:
;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' AS i,
'http://schemas.datacontract.org/2004/07/TriTech.InformRMS.Domain.Core.ComplexTypes' AS s)
SELECT TOP 100
TargetData_Xml,
Description,
TargetData_Xml.value('(/s:ArrayOfTarget/s:Target/s:Name)[1]', 'varchar(100)') as Context1,
TargetData_Xml.value('(/s:ArrayOfTarget/s:Target/s:Name)[2]', 'varchar(100)') as Context2,
TargetData_Xml.value('(/s:ArrayOfTarget/s:Target/s:Name)[3]', 'varchar(100)') as Context3,
TargetData_Xml.value('(/s:ArrayOfTarget/s:Target/s:Name)[4]', 'varchar(100)') as Context4,
TargetData_Xml.value('(/s:ArrayOfTarget/s:Target/s:Name)[5]', 'varchar(100)') as Context5
FROM
[InformRMSAudit].[dbo].[AuditEntities]
WHERE
CaseNumber = 'RP21-010802'
ORDER BY
Date desc
That would be sufficient if every record had only five "Name" elements in the XML, but the number of "Name" elements varies from record to record.
How could I change my query to handle the variation from record to record?
Please try the following solution.
As #Larnu pointed out, it is much better to shred the XML as rows.
If needed it is very easy to filter out names with NULL values.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, TargetData_Xml XML);
INSERT INTO #tbl (TargetData_Xml) VALUES
(N'<ArrayOfTarget xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/TriTech.InformRMS.Domain.Core.ComplexTypes">
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true"/>
<Id>19bc33e1-a788-cd99-3dab-08d92aa7d030</Id>
<Name>Case Number WB21-006637</Name>
<Type>Case Management</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true"/>
<Id>cb4d829b-c31a-cadb-5c9e-08d934b7404d</Id>
<Name>Incident Supplement Number WB21-006637.006</Name>
<Type>Data Entry</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true"/>
<Id>6b23195c-4650-c0c9-925d-08d71a88f611</Id>
<Name i:nil="true"/>
<Type>Template</Type>
</Target>
<Target>
<AgencyId>ca2fa1dd-2cd4-c219-bea5-08d6fbe6d96c</AgencyId>
<AgencyName i:nil="true"/>
<Id>b465517c-5926-c6b3-1cc6-08d6fbe6da27</Id>
<Name>Default Workflow</Name>
<Type>Workflow</Type>
</Target>
<Target>
<AgencyId i:nil="true"/>
<AgencyName i:nil="true"/>
<Id i:nil="true"/>
<Name>Complete</Name>
<Type>Workflow Step</Type>
</Target>
</ArrayOfTarget>');
-- DDL and sample data population, end
WITH XMLNAMESPACES (DEFAULT 'http://schemas.datacontract.org/2004/07/TriTech.InformRMS.Domain.Core.ComplexTypes')
SELECT ID
, c.value('(Name/text())[1]', 'VARCHAR(100)') AS [Name]
FROM #tbl
CROSS APPLY TargetData_Xml.nodes('/ArrayOfTarget/Target') AS t(c);
Output
+----+--------------------------------------------+
| ID | Name |
+----+--------------------------------------------+
| 1 | Case Number WB21-006637 |
| 1 | Incident Supplement Number WB21-006637.006 |
| 1 | NULL |
| 1 | Default Workflow |
| 1 | Complete |
+----+--------------------------------------------+

Find the value from XML data in SQL Server?

I want to find the value from XML data in SQL Server table.
Below is my sample xmldata;
<wddxPacket version="1.0">
<header />
<data>
<struct type="xyz">
<var name="TXRGHC43">
<string />
</var>
<var name="TWBS1">
<string>9011750</string>
</var>
<var name="PMNAMEID">
<string>2323443</string>
</var>
<var name="EDATE36">
<string />
</var>
<var name="TWBSDESC40">
<string />
</var>
</struct>
</data>
</wddxPacket>
I am searching for 9011750 under TWBS1. can you please help me on this. how to find 9011750 value.
I am trying the following queries but I didn't get any output.
select
col
from
xyz
where
col.value('(/wddxPacket/data/struct/var[TWBS1])[2]', 'varchar(max)') like '9011750'
SELECT col.value('(/wddxPacket/data/struct/var/string)[3]', 'varchar(100)')
FROM xyz
my requirement is to search the data entire table, that is only sample xml data.
Please try the following SQL. It shows how to use a predicate in the XPath expression to simulate WHERE clause.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO #tbl (xmldata)
VALUES
(N'<wddxPacket version="1.0">
<header/>
<data>
<struct type="xyz">
<var name="TXRGHC43">
<string/>
</var>
<var name="TWBS1">
<string>9011750</string>
</var>
<var name="PMNAMEID">
<string>2323443</string>
</var>
<var name="EDATE36">
<string/>
</var>
<var name="TWBSDESC40">
<string/>
</var>
</struct>
</data>
</wddxPacket>');
-- DDL and sample data population, end
DECLARE #varName VARCHAR(20) = 'TWBS1';
SELECT c.value('(./text())[1]','INT') AS [string_value]
FROM #tbl AS tbl
CROSS APPLY tbl.xmldata.nodes('/wddxPacket/data/struct/var[#name=sql:variable("#varName")]/string') AS t(c);
Output
+--------------+
| string_value |
+--------------+
| 9011750 |
+--------------+

Read XML attributes in postgres

I am using XML first timer in Postgres and facing an issue.
I have below xml in a variable named XMLCONTENT
<?xml version="1.0" encoding="UTF-8"?>
<Actions>
<Action ActionID="90e0dbef-c23a-4fcd-bfa8-75d8bfa2c9e2" />
<Action ActionID="6a1998e1-70f1-4611-992a-7a27e2834c35" />
<Action ActionID="43dd9a91-c6d3-4980-b211-9b3780f04305" />
<Action ActionID="cdf01821-ac28-45a9-abf8-a7d7c9426518" />
<Action ActionID="e86fac8a-84e3-41ba-8bee-c7ffd1ac8ee5" />
<Action ActionID="a68dd878-ba1e-4fd9-b436-cdc15eccffb6" />
<Action ActionID="cd863a5a-83e9-489e-b24d-ff6638c5b190" />
<Action ActionID="720ba9c7-b797-4b2e-913e-11ac3ecd7b7f" />
<Action ActionID="b6b35d0d-938e-45d3-96d1-0c8ca3ad59f3" MessageID="42f40c3a-4426-4506-86c5-222fb03c2114" />
</Actions>
I want to extract details from this XML and I am using below query
Select
Unnest(xpath('//#ActionID',XMLCONTENT)) as ID,
Unnest(xpath('//#MessageID',XMLCONTENT)) as MessageID,
Unnest(xpath('//#Operator',XMLCONTENT)) as Operator
but i am getting wrong output as shown below
MessageID is linked with the wrong actionID. What is the correct way to traverse this XML?
The reason your query is not working is the use of unnest() in the select list: each unnest() call adds a new row to the result.
You need to use unnest in the from clause to create one row for each <Action> element:
with data (xmlcontent) as (
values ('
<Actions>
<Action ActionID="90e0dbef-c23a-4fcd-bfa8-75d8bfa2c9e2" />
<Action ActionID="6a1998e1-70f1-4611-992a-7a27e2834c35" />
<Action ActionID="43dd9a91-c6d3-4980-b211-9b3780f04305" />
<Action ActionID="cdf01821-ac28-45a9-abf8-a7d7c9426518" />
<Action ActionID="e86fac8a-84e3-41ba-8bee-c7ffd1ac8ee5" />
<Action ActionID="a68dd878-ba1e-4fd9-b436-cdc15eccffb6" />
<Action ActionID="cd863a5a-83e9-489e-b24d-ff6638c5b190" />
<Action ActionID="720ba9c7-b797-4b2e-913e-11ac3ecd7b7f" />
<Action ActionID="b6b35d0d-938e-45d3-96d1-0c8ca3ad59f3"
MessageID="42f40c3a-4426-4506-86c5-222fb03c2114" />
</Actions>'::xml)
)
select (xpath('//#ActionID', xt.action))[1] as id,
(xpath('//#MessageID', xt.action))[1] as message_id
from data
cross join unnest(xpath('/Actions/Action', xmlcontent)) as xt(action);
Returns:
id | message_id
-------------------------------------+-------------------------------------
90e0dbef-c23a-4fcd-bfa8-75d8bfa2c9e2 |
6a1998e1-70f1-4611-992a-7a27e2834c35 |
43dd9a91-c6d3-4980-b211-9b3780f04305 |
cdf01821-ac28-45a9-abf8-a7d7c9426518 |
e86fac8a-84e3-41ba-8bee-c7ffd1ac8ee5 |
a68dd878-ba1e-4fd9-b436-cdc15eccffb6 |
cd863a5a-83e9-489e-b24d-ff6638c5b190 |
720ba9c7-b797-4b2e-913e-11ac3ecd7b7f |
b6b35d0d-938e-45d3-96d1-0c8ca3ad59f3 | 42f40c3a-4426-4506-86c5-222fb03c2114
In the select list, you know that each '//#ActionID' only returns a single element, so there is no need to use unnest at that level any more.
Online example: https://rextester.com/MWBCEN37238
If you were using Postgres 10 or later, this would be a bit simpler with XMLTABLE:
select xt.*
from data
cross join xmltable ('/Actions/Action'
passing xmlcontent
columns id uuid path '#ActionID',
message_id uuid path '#MessageID'
) as xt;
Online example: https://dbfiddle.uk/?rdbms=postgres_10&fiddle=1e70be54c25a42db5ebff9a996423920

XML generate from SQL Query with perfect XML structure

MY SQl "employee" Table Look Like
+-------+---------+--------+----------+
| Empid | Empname | Salary | Location |
+-------+---------+--------+----------+
| 1 | Arul | 100 | Chennai |
+-------+---------+--------+----------+
XML Generate from SQl Query:
select * from employee for xml path, root('root')
from this Sql Query I'm Getting My XML Files as given below
<root>
<employee>
<Empid>1</Empid>
<Empname>Arul</Empname>
<Salary>100</Salary>
<Location>Chennai</Location>
</employee>
</root>
But My Expected Output XML from SQL query as
<root>
<column>Empid</column>
<value>1</value>
<column>Empname</column>
<value>Arul</value>
</root>
As you were told already, the needed output format is really bad and erronous. Nevertheless this can be done quite easily:
DECLARE #mockup TABLE(Empid INT,Empname VARCHAR(100),Salary DECIMAL(10,4),[Location] VARCHAR(100));
INSERT INTO #mockup VALUES(1,'Arul',100,'Chennai')
,(2,'One',200,'More');
SELECT 'Empid' AS [Column]
,EmpId AS [Value]
,'Empname' AS [Column]
,Empname AS [Value]
-- follow this pattern...
FROM #mockup t
FOR XML PATH('employee'),ROOT('root');
The result
<root>
<employee>
<Column>Empid</Column>
<Value>1</Value>
<Column>Empname</Column>
<Value>Arul</Value>
</employee>
<employee>
<Column>Empid</Column>
<Value>2</Value>
<Column>Empname</Column>
<Value>One</Value>
</employee>
</root>
But - by any chance - you should try to change this format. This is awful to query and will be your private headache for sure...
Some better suggestions:
<Employee>
<Column name="EmpId" value="1" />
<Column name="Empname" value="Arul" />
</Employee>
or
<employee id="1" name="Arul" />
or
<employee>
<Id>1</Id>
<Name>Arul</Name>
</employee>
or (if you really, really want to stick with this), at least a column index like here
<root>
<employee>
<Column inx="1">Empid</Column>
<Value inx="1">1</Value>
<Column inx="2">Empname</Column>
<Value inx="2">Arul</Value>
</employee>
<employee>
<Column inx="1">Empid</Column>
<Value inx="1">2</Value>
<Column inx="2">Empname</Column>
<Value inx="2">One</Value>
</employee>
</root>
The query for the last one is this
SELECT 1 AS [Column/#inx]
,'Empid' AS [Column]
,1 AS [Value/#inx]
,EmpId AS [Value]
,2 AS [Column/#inx]
,'Empname' AS [Column]
,2 AS [Value/#inx]
,Empname AS [Value]
-- follow this pattern...
FROM #mockup t
FOR XML PATH('employee'),ROOT('root');

Delete Empty tag from xmltype oracle

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