I have to shred XML with unknown schema to a table. I don't know what elements there are in XML. And I am also not aware of XML format. In some cases XML data is Attribute centric and in some cases it is element centric.
For Example -
I have two XML -
<Root>
<Recorset>
<RecordsetId>1</RecordsetId>
<RecordsetName>name1</RecordsetName>
</Recorset>
</Root>
AND
<Root>
<Recorset RecordsetId="2" RecordsetName="name2"></Recorset>
</Root>
XML can have any other element/attributes. I need to capture the elements/attributes name and respective data using EDGE table produced from OPENXML.
What is the simplest way of doing this?
I need output in given format -
RecodrsetId RecordsetName
1 Name1
2 Name2
something like this?
select
C.Name,
C.Value
from #Data.nodes('//*') as T(C)
outer apply (
select
T.C.value('local-name(.)', 'nvarchar(max)') as Name,
T.C.value('(./text())[1]', 'nvarchar(max)') as Value
union all
select
A.C.value('local-name(.)', 'nvarchar(max)') as Name,
A.C.value('.', 'nvarchar(max)') as Value
from T.C.nodes('#*') as A(C)
) as C
where C.Value is not null
sql fiddle demo
Related
SELECT *
FROM #myHierarchy
FOR XML AUTO
Data is
<_x0040_myHierarchy element_id="1" parent_ID="1" NAME="itemCode" StringValue="Simmi" ValueType="string" />
I'm unable to load data in this query
SELECT #xml = dbo.ToXML(#myHierarchy);
SELECT
a.b.value('#ItemCode', 'varchar(20)') AS ItemCode
FROM
#xml.nodes('/root/_x0040_myHierarchy') a(b)
In this query, itemcode is blank. How can I load data using this query?
Your sample XML does not contain any attribute ItemCode - it has these attributes:
element_id
parent_ID
NAME
StringValue
ValueType
So which value do you really want to read out from the XML element?
Update: to retrieve the StringValue attribute, use this code:
SELECT
XC.value('#StringValue', 'varchar(50)')
FROM
#xml.nodes('/_x0040_myHierarchy') AS XT(XC)
If your XML contains a <root> ..... </root> root element, and multiple <_x0040_myHierarchy> elements inside, and you want to extract the one with #Name = 'itemCode' - then you need to use this SELECT:
SELECT
XC.value('#StringValue', 'varchar(50)')
FROM
#xml.nodes('/root/_x0040_myHierarchy') AS XT(XC)
WHERE
XC.value('#NAME', 'varchar(50)') = 'itemCode'
I'm outputting a XML formatted response in a select statement.
The XML schema goes: Placemark->MultiGeometry->Polygon-><more children>
I'm using existing data.
The table I am running with, let's call it table1, has a name (varchar datatype), style (varchar datatype), and geometry (XML datatype)
The values of the geometry field have pre-formatted xml already. The root tag for each value for all records is MultiGeometry.
Here's the problem.
If I perform:
select name, style, geometry from table1
for xml path('Placemark'), ELEMENTS
I receive this:
<Placemark>
<Name>stuff</Name>
<style>style stuff</style>
<Geometry>
<MultiGeometry>...xml...</MultiGeometry>
</Geometry>
</Placemark>
This fails schema, because the schema (kml schemas) require that MultiGeometry is the child tag inside PLacemark, and the schema does not recognize the <Geometry> tag.
I need this:
<Placemark>
<Name>stuff</Name>
<style>style stuff</style>
<MultiGeometry>...xml...</MultiGeometry>
</Placemark>
but trying:
select name, style, geometry as [ ]
from table1 for xml path('Placemark'), ELEMENTS
fails the query.
How do I get to where I can put MultiGeometry in Placemark without a intermediate tag (Such as <Geometry>)?
Thanks much
Sure:
select name, style, geometry.query('.')
from table1
for xml path('Placemark'), type, ELEMENTS;
Maybe something like.....
SELECT CAST(
REPLACE(
REPLACE(CAST((select name, style, [Geometry] AS [ReplaceMe]
from Table1
for xml path('Placemark'),ELEMENTS) AS VARCHAR(MAX))
, '<ReplaceMe>' , '')
, '</ReplaceMe>' , '') AS XML)
The FOR XML command uses column names to create the XML nodes. I need my nodes to be named after values I'm getting from the database. As far as I know you can't do something such as
SELECT Key AS Section
SECTION | KEY | VALUE
-----------------------------------------------------------
PageAddProduct | ErrorDateFormat | Incorrect value
PageAddProduct | ErrorNotSelected | Please select value
WidgetLogin | Title | Connexion
WidgetLogin | MailLabel | Mail
This is the desired XML output
<Resources>
<WidgetLogin>
<Title>Connexion</Title>
<MailLabel>Mail</MailLabel>
</WidgetLogin>
</Resources>
Not sure if you can get this with For Xml. Help would be much appreciated.
You can build your XML as a string and then cast to XML.
This sample code will work in SQL Server 2012 since it is using the concat function but it can easily be rewritten to use + instead.
select cast(concat('<Resources>',
(
select concat('<',T1.SECTION,'>',
(
select concat('<',T2.[KEY],'>',
(select T2.VALUE for xml path('')),
'</',T2.[KEY],'>')
from T as T2
where T1.SECTION = T2.SECTION
for xml path(''), type
).value('text()[1]', 'nvarchar(max)'),
'</',T1.SECTION,'>')
from T as T1
group by T1.SECTION
for xml path(''), type
).value('text()[1]', 'nvarchar(max)'),
'</Resources>') as xml)
SQL Fiddle
You can use FOR XML like below:
SELECT Section as Title, Value as MailLabel
FROM table
FOR XML PATH('WidgetLogin'), ROOT('Resources'), TYPE
I have two different pieces of XML to put together.
For example, SQL for the first piece looks like this:
SELECT
*
FROM
(
SELECT 1 AS OrdNum, 'Abc' AS Name
) a
FOR XML
AUTO,
TYPE
Once executed, you'll get this:
<a OrdNum="1" Name="Abc" />
The second one is here:
SELECT
*
FROM
(
SELECT 4 AS Age, 'M' AS Sex, 'John' AS FirstName
) b
FOR XML
AUTO,
TYPE
You'll get this:
<b Age="4" Sex="M" FirstName="John" />
Now I'll put the two pieces together:
SELECT
*
FROM
(
SELECT
(
SELECT
*
FROM
(
SELECT 1 AS OrdNum, 'Abc' AS Name
) a
FOR XML
AUTO,
TYPE
) AS aa
,
(
SELECT
*
FROM
(
SELECT 4 AS Age, 'M' AS Sex, 'John' AS FirstName
) b
FOR XML
AUTO,
TYPE
) AS bb
) Data
FOR XML
AUTO,
ELEMENTS
The result is as follows:
<Data>
<aa>
<a OrdNum="1" Name="Abc" />
</aa>
<bb>
<b Age="4" Sex="M" FirstName="John" />
</bb>
</Data>
But I do not want to have the elements "aa" and "bb" there. I'd love to get this:
<Data>
<a OrdNum="1" Name="Abc" />
<b Age="4" Sex="M" FirstName="John" />
</Data>
But I have no idea how to achieve that.
Any hints?
There is no "simple" way to do it. FOR XML PATH|EXPLICIT|AUTO will all require each top-level, output element to have the same name. And you can't UNION multiple FOR XML queries together (Sql Server 2012).
The direction you went in is the most reliable and flexible. Essentially, you have to add a separate column for each different element type you want to include. You could simplify your final attempt to this to get what you wanted:
SELECT
(
SELECT 1 AS [#OrdNum], 'Abc' AS [#Name]
WHERE 1=1
FOR XML PATH ('a'), TYPE
)
,
(
SELECT 4 AS [#Age], 'M' AS [#Sex], 'John' AS [#FirstName]
WHERE 1=1
FOR XML PATH ('b'), TYPE
)
FOR XML PATH ('Data'), TYPE;
The above query outputs:
<Data>
<a OrdNum="1" Name="Abc" />
<b Age="4" Sex="M" FirstName="John" />
</Data>
When you use FOR XML PATH, the column aliases are XPaths. So to make it an attribute name, you have to prefix with '#'---which then requires you to escape the alias (hence the []). The parameter on PATH dictates the name of each row's Xml element. The TYPE option says to keep the output as type Xml instead of nvarchar(max), which means that the outer query can merge it better. And the outer query just has 2 columns to stuff into the single element it represents. Finally, I like the WHERE 1=1, but it's not syntactically required.
A tangent: I know your example is simplified, so you may wish to know that Xml data types can have "methods" applied to them. For example, say you wanted the above, but an outer query only needed the "b" elements. You could use the query() method to select only parts of the Xml to merge into some outer query.
SELECT
(
SELECT
(
SELECT 1 AS [#OrdNum], 'Abc' AS [#Name]
WHERE 1=1
FOR XML PATH('a'), TYPE
)
,
(
SELECT 4 AS [#Age], 'M' AS [#Sex], 'John' AS [#FirstName]
WHERE 1=1
FOR XML PATH('b'), TYPE
)
FOR XML PATH('Data'), TYPE
).query('Data/b');
Which produces this:
<b Age="4" Sex="M" FirstName="John" />
You need to look at the FOR XML PATH option that SQL Server 2005 introduced - see the What's New in FOR XML in Microsoft SQL Server 2005 document for more information.
Basically, with FOR XML PATH, you can define the shape of your XML very easily. You can define certain structures, you can define certain columns to be output as attributes, and others as elements - totally under your control.
You can get more information on how to format that here:
enter link description here
I am using the below query to select the values of XML from attributes ad elements of the XML file but I am not able to read the seq, id, reported dated attributes from XML page
so any one please suggest How to get values of attributes using this Query.
select a_node.value('(./text())[1]', 'var char(50)') AS c_val,
c1_node.value('(./text())[1]', 'var char(50)') AS c_val 2,
ca_node.value('(./text())[1]', 'var char(50)') AS c_val3,
d_node.value('(./text())[1]', 'var char(50)') ,
e_node.value('(./text())[1]', 'varchar(50)') ,
f_node.value('(./text())[1]', 'var char(50)')
FROM #xmlData.nodes('/Reports/x:InquiryResponse/x:ReportData/x:AccountDetails/x:Account') AS b(b_node)
outer APPLY b.b_node.nodes('./x:primarykey') AS pK_InquiryResponse (a_node)
outer APPLY b.b_node.nodes('./x:seq') AS CustomerCode (c1_node)
outer APPLY b.b_node.nodes('./x:id') AS amount (ca_node)
outer APPLY b.b_node.nodes('./x:ReportedDate') AS CustRefField (d_node)
outer APPLY b.b_node.nodes('./x:AccountNumber') AS ReportOrderNO (e_node)
outer apply b.b_node.nodes('./x:CurrentBalance') as additional_id (f_node);
Edit: Xml Snippets Provided in Comments
<sch:Account seq="2" id="345778174" ReportedDate="2014-01-01">
<sch:AccountNumber>TSTC1595</sch:AccountNumber>
<sch:CurrentBalance>0</sch:CurrentBalance>
<sch:Institution>Muthoot Fincorp Limited</sch:Institution>
<sch:PastDueAmount>0</sch:PastDueAmount>
<sch:DisbursedAmount>12000</sch:DisbursedAmount>
<sch:LoanCategory>JOG Group</sch:LoanCategory>
</sch:Account>
<sch:Account seq="2" id="345778174" ReportedDate="2014-01-01">
<sch:BranchIDMFI>THRISSUR ROAD</sch:BranchIDMFI>
<sch:KendraIDMFI>COSTCO/RECENT-107</sch:KendraIDMFI>
</sch:Account>
Parsing XQuery with an Xml Loose #Variable
Assuming an Xml document similar to this (viz with all the attributes on one element):
DECLARE #xmlData XML =
N'<Reports xmlns:x="http://foo">
<x:InquiryResponse>
<x:ReportData>
<x:AccountDetails>
<x:Account x:primarykey="pk" x:seq="sq" x:id="id"
x:ReportedDate="2014-01-01T00:00:00" />
</x:AccountDetails>
</x:ReportData>
</x:InquiryResponse>
</Reports>';
You can scrape the attributes out as follows:
WITH XMLNAMESPACES('http://foo' AS x)
select
Nodes.node.value('(#x:primarykey)[1]', 'varchar(50)') AS c_val,
Nodes.node.value('(#x:seq)[1]', 'varchar(50)') AS c_val2,
Nodes.node.value('(#x:id)[1]', 'varchar(50)') AS c_val3,
Nodes.node.value('(#x:ReportedDate)[1]', 'DATETIME') as someDateTime
FROM
#xmlData.nodes('/Reports/x:InquiryResponse/x:ReportData/x:AccountDetails/x:Account')
AS Nodes(node);
Attributes don't need text() as they are automatically strings
It is fairly unusual to have attributes in a namespace - drop the xmlns alias prefix if they aren't.
SqlFiddle here
Edit - Parsing Xml Column
Namespace dropped from the attributes
-Assumed that you have the data in a table, not a variable, hence the APPLY requirement. Note that OUTER APPLY will return nulls, e.g. useful only if you have rows with
empty Xml or missing Xml Elements. CROSS APPLY is the norm (viz
applying the xpath to each row selected on the LHS table)
Elements are accessed similar to attributes, just without #
WITH XMLNAMESPACES('http://foo' AS x)
select
Nodes.node.value('(#seq)[1]', 'varchar(50)') AS c_val2,
Nodes.node.value('(#id)[1]', 'varchar(50)') AS c_val3,
Nodes.node.value('(#ReportedDate)[1]', 'DATETIME') as someDateTime,
Nodes.node.value('(x:AccountNumber)[1]', 'VARCHAR(50)') as accountNumber
FROM
MyXmlData z
CROSS APPLY
z.XmlColumn.nodes('/Reports/x:InquiryResponse/x:ReportData/x:AccountDetails/x:Account')
AS Nodes(node);
Updated Fiddle
Edit Xml File off Disk
Here's the same thing for an xml file read from disk. Note that once you have the data in an XML variable (#MyXmlData) that you don't need to CROSS APPLY to anything - just supply xpath to select the appropriate node, and then scrape out the elements and attributes.
DECLARE #MyXmlData XML;
SET #MyXmlData =
( SELECT * FROM OPENROWSET ( BULK N'c:\temp\file3098.xml', SINGLE_CLOB ) AS MyXmlData );
-- Assuming all on the one element, no need for all the applies
-- attributes don't have a text axis (they are automatically strings
WITH XMLNAMESPACES('http://foo' AS x)
select
Nodes.node.value('(#seq)[1]', 'varchar(50)') AS c_val2,
Nodes.node.value('(#id)[1]', 'varchar(50)') AS c_val3,
Nodes.node.value('(#ReportedDate)[1]', 'DATETIME') as someDateTime,
Nodes.node.value('(x:AccountNumber)[1]', 'VARCHAR(50)') as accountNumber
FROM
#MyXmlData.nodes('/Reports/x:InquiryResponse/x:ReportData/x:AccountDetails/x:Account')
AS Nodes(node);