SQL Server 2012. Given the following XML:
<header>
<subheader>
<Problems>
<RAW VALUE="1" Sublot="abc"/>
<RAW VALUE="2" Sublot="def"/>
<RAW VALUE="3" Sublot="ghi"/>
</Problems>
</subheader>
</header>
how does one parse the XML in SQL Server? I an having problems dealing with "RAW VALUE" as a name, such that
SELECT *
FROM OPENXML (#docHandle, '/header/subheader/Problems', 1)
WITH (
'RAW VALUE' VARCHAR (100)
);
is invalid because of the quote around 'RAW VALUE'. Brackets don't work either. Is there a way to support the space in "RAW VALUE"?
The attributes of XML elements are accessed with the # XPath expression prefix, e.g.: #VALUE for the VALUE attribute, #Sublot for the Sublot attribute...
declare #example xml = N'<header>
<subheader>
<Problems>
<RAW VALUE="1" Sublot="abc"/>
<RAW VALUE="2" Sublot="def"/>
<RAW VALUE="3" Sublot="ghi"/>
</Problems>
</subheader>
</header>';
select
prob.raw.value('#VALUE', N'nvarchar(100)') as [Value],
prob.raw.value('#Sublot', N'nvarchar(100)') as [Sublot]
from #example.nodes('/header/subheader/Problems/RAW') prob(raw);
Which yields the result:
Value
Sublot
1
abc
2
def
3
ghi
Related
I have a table named "Table1" and in the table there are columns titled "Name" and "XMLDefinition".
Name XMLDefinition
--------------------------
Name1 xmlLink1
Name2 xmlLink2
Inside each XML an example would look similar to this below:
<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<QueryView>
<QueryKey />
</QueryView>
<Description>
</Description>
<QueryFields>
<f />
<f />
</QueryFields>
<FilterFields>
<f ObjectName="TABLE5" ColumnName="ID">
<DateFilterTypes />
<FuzzyDateFilterTypes />
<MonthDayFilterTypes />
<Values>
<v>0</v>
</Values>
<TranslatedValues>
<v>No</v>
</TranslatedValues>
<DataType>Boolean</DataType>
</f>
<f ObjectName="TABLE2" ColumnName="USERID">
<DateFilterTypes />
<FuzzyDateFilterTypes />
<MonthDayFilterTypes />
<Values>
<v>B80055</v>
</Values>
<TranslatedValues>
<v>B80055</v>
</TranslatedValues>
<DataType>String</DataType>
</f>
</FilterFields>
</Query>
I'd like to return Name from TABLE1 as long as in the XML content contains where ObjectName = "TABLE2" AND ColumnName = "USERID".
I have tried the below and while it doesn't error out, it returns 0 records:
SELECT
a.Name,
X.Y.value('(f)[1]', 'VARCHAR(MAX)') as Object
FROM TABLE1 a
OUTER APPLY a.XMLDefinition.nodes('Query/FilterFields/f') as X(Y)
WHERE X.Y.value('(ObjectName)[1]', 'VARCHAR(MAX)') = 'TABLE2'
AND X.Y.value('(ColumnName)[1]', 'VARCHAR(MAX)') = 'USERID'
I'm not sure what I am missing as it seems I am drilling down from Query > FilterFields > f and I assume I'd be able to then filter based on the ObjectName and ColumnName here.
Attempt 2 Update:
SELECT Name from TABLE1
WHERE XMLDefinition.value('(/Query/QueryView/Description/QueryFields/FilterFields/f/#ObjectName) [1] ',' varchar(max)') = 'TABLE2'
AND XMLDefinition.value('(/Query/QueryView/Description/QueryFields/FilterFields/f/#ColumnName) [1] ',' varchar(max)') = 'USERID'
After trying this attempt by drilling through each tag, it is still giving me 0 results.
Attempt 3 Update:
select
a.Name,
X.Y.query(N'.') as [Object] --this returns the XML of the <f> element
from dbo.Table1 a
cross apply a.XMLDefinition.nodes('//*:f[#ObjectName="TABLE2"][#ColumnName="USERID"][1]') as X(Y);
I'm not sure why, but I tried this and now it worked and returned the results that I was looking for. I'm new to XML, but I assume this worked because it ignored all the namespaces and prior tags before the f tag?
The code below probably does what you're looking for. Note that cross apply will only return dbo.Table rows that matched the XPath query, as opposed to outer apply which will return all dbo.Table rows but only XML-derived values for those rows that matched the XPath query:
create table dbo.Table1 (
Name nvarchar(10),
XMLDefinition xml
);
insert dbo.Table1 (Name, XMLDefinition) values
(N'Name1', N'<xmlLink1 />'),
(N'Name2', N'<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FilterFields>
<f ObjectName="TABLE2" ColumnName="USERID" ParentPath="TABLE2" DisplayPath="TABLE2" CompareType="Or" UseLeftParenthesis="true" LeftParenthesisCount="1" IncludeCurrentNode="true">
<DateFilterTypes />
<FuzzyDateFilterTypes />
<MonthDayFilterTypes />
<Values>
<v>B80055</v>
</Values>
<TranslatedValues>
<v>B80055</v>
</TranslatedValues>
<DataType>String</DataType>
</f>
</FilterFields>
</Query>');
select
a.Name,
X.Y.query(N'.') as [Object] --this returns the XML of the <f> element
from dbo.Table1 a
cross apply a.XMLDefinition.nodes(N'/Query/FilterFields/f[#ObjectName="TABLE2"][#ColumnName="USERID"][1]') as X(Y);
There are 3 attributes I am trying to return when reading this xml file. 2 of the 3 are returning expected values. I can not figure out why I can't get the id to return the value. It always returns NULL. How can I get the proper value of rid1 and rid2
DECLARE #xml xml
SELECT #xml = BulkColumn
FROM OPENROWSET(BULK 'C:\data\workbook.xml', SINGLE_BLOB) x;
WITH XMLNAMESPACES (default
'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships' as a,
'http://schemas.openxmlformats.org/markup-compatibility/2006' as b,
'http://schemas.microsoft.com/office/spreadsheetml/2010/11/main' as c,
'http://schemas.microsoft.com/office/spreadsheetml/2010/11/ac' as d)
SELECT
doc.col.value('#name', 'nvarchar(10)') sheet
,doc.col.value('#id', 'nvarchar(max)') rid
,doc.col.value('#sheetId', 'int') id
FROM #xml.nodes('*:workbook/sheets/sheet') doc(col)
Here is the XML File
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main" mc:Ignorable="x15">
<fileVersion appName="xl" lastEdited="7" lowestEdited="7" rupBuild="18431" />
<workbookPr defaultThemeVersion="166925" />
<mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<mc:Choice Requires="x15">
<x15ac:absPath xmlns:x15ac="http://schemas.microsoft.com/office/spreadsheetml/2010/11/ac" url="C:\data\" />
</mc:Choice>
</mc:AlternateContent>
<bookViews>
<workbookView xWindow="0" yWindow="0" windowWidth="21576" windowHeight="7968" />
</bookViews>
<sheets>
<sheet name="Sheet1" sheetId="1" r:id="rId1" />
<sheet name="Sheet2" sheetId="2" r:id="rId2" />
</sheets>
<calcPr calcId="171027" />
<fileRecoveryPr repairLoad="1" />
<extLst>
<ext xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main" uri="{140A7094-0E35-4892-8432-C4D2E57EDEB5}">
<x15:workbookPr chartTrackingRefBase="1" />
</ext>
</extLst>
</workbook>
You need to prefix the attribute with the namespace like so:
SELECT
doc.col.value('#name', 'nvarchar(10)') sheet
,doc.col.value('#a:id', 'nvarchar(max)') rid
,doc.col.value('#sheetId', 'int') id
FROM #xml.nodes('*:workbook/sheets/sheet') doc(col)
Give your namespaces the same aliases in your T-SQL as they have in your XML and use the namespace alias in your reference:
WITH XMLNAMESPACES (DEFAULT 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
'http://schemas.openxmlformats.org/officeDocument/2006/relationships' AS r,
'http://schemas.openxmlformats.org/markup-compatibility/2006' AS mc,
'http://schemas.microsoft.com/office/spreadsheetml/2010/11/main' AS x15,
'http://schemas.microsoft.com/office/spreadsheetml/2010/11/ac' AS x15ac)
SELECT doc.col.value('#name', 'nvarchar(10)') sheet,
doc.col.value('#r:id', 'nvarchar(max)') rid,
doc.col.value('#sheetId', 'int') id
FROM #xml.nodes('*:workbook/sheets/sheet') doc(col);
How to convert the below string to XML,
SET #string = '<Field>
<Field Name="'+#Cname+'">
<DataField>'+#Cname+'</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>'
I tried SET #xmlstring = CONVERT(XML,#string) but it displays below error
Msg 9459, Level 16, State 1, Line 17
XML parsing: line 4, character 13, undeclared prefix
Oh no! Never create XML via string concatenation! Just imagine, your variable comes with a value like this -> value or Tim, Tom & Fred... This might work perfectly, pass all tests and break after rollout with undefined errors.
Always use SQL-Servers support to create XML:
DECLARE #cname VARCHAR(100)='Some Name';
DECLARE #xml XML=
(
SELECT #cname AS [Field/#Name]
,#cname AS [DataField]
,'System.String' AS [TypeName]
FOR XML PATH('Field')
);
SELECT #xml;
The result
<Field>
<Field Name="Some Name" />
<DataField>Some Name</DataField>
<TypeName>System.String</TypeName>
</Field>
And here with the namespace:
WITH XMLNAMESPACES('Some.namespace.url' AS rd)
SELECT #xml=
(
SELECT #cname AS [Field/#Name]
,#cname AS [DataField]
,'System.String' AS [rd:TypeName]
FOR XML PATH('Field')
);
SELECT #xml
The result
<Field xmlns:rd="Some.namespace.url">
<Field Name="Some Name" />
<DataField>Some Name</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
Your xml need formatted correctly.
try to close tag Field with prop Name using '/' and take out prefix rd in TypeName.
SET #string = '<Field>
<Field Name="'+#Cname+'" />
<DataField>'+#Cname+'</DataField>
<TypeName>System.String</TypeName>
</Field>'
http://sqlfiddle.com/#!18/9eecb/8282/0
I'm working with XML data and want to return the values of two child nodes. I can get this work with a single node but not return two seperate columns. How do I write the xPath and xQuery to return two columns?
DECLARE #x XML
Set #x = '
<MT_BoxTextCtrl>
<DataSpec>ShipID_3_1_1</DataSpec>
<Label>Mode</Label>
<Size>230,30</Size>
<Units />
<UserLoValue />
</MT_BoxTextCtrl>
<MT_BoxTextCtrl>
<DataSpec>ShipID_3_1_2</DataSpec>
<Label>Sub Mode</Label>
<Size>230,30</Size>
<Units />
<UserLoValue />
</MT_BoxTextCtrl>
<MT_AlarmCtrl>
<AlarmRngIsReversed>False</AlarmRngIsReversed>
<CustomQuery />
<DataSpec>ShipID_9_1_1</DataSpec>
<HiValue>1</HiValue>
<Label />
</MT_AlarmCtrl>
<MT_AlarmCtrl>
<AlarmRngIsReversed>False</AlarmRngIsReversed>
<CustomQuery />
<DataSpec>ShipID_9_1_5</DataSpec>
<HiValue>1</HiValue>
<Label>In 500M DP Zone</Label>
</MT_AlarmCtrl>'
Select T.c.value('.', 'varchar(30)') as 'DataSpec'
from #x.nodes('//DataSpec') T(c)
Select T.c.value('.', 'varchar(30)') as Label
from #x.nodes('//Label') T(c)
thanks
Try this:
SELECT
NodeType = XC.value('local-name(.)', 'varchar(25)'),
DataSpec = XC.value('(DataSpec)[1]', 'varchar(30)'),
Label = XC.value('(Label)[1]', 'varchar(30)')
FROM
#x.nodes('/*') XT(XC)
This basically takes every top-level node and returns a "virtual" table of those XML fragments. From those XML fragments, I grab the node name (the "type" of the XML node in question), the DataSpec and the Label subelements (their textual values).
I get an output like this:
I have a scalar xml variable:
DECLARED #XML xml =
'<rows>
<row>
<column1 attrib="" />
<column2 attrib="" />
</row>
<!-- ... -->
</rows>';
I would like to split the data so that each row's xml is assigned to a new record in a table:
Id | ... | XML
1 | | '<row><column1 attrib="" /><column2 attrib="" /></row>'
2 | | etc.
3 | | etc.
I haven't quite grasped xquery so I'm having trouble writing an insert statement that does what I want.
INSERT into MyTable( [XML])
SELECT #XML.query('row')
Now I know something is happening but it appears rather than doing what I intended and inserting multiple new records it is inserting a single record with an empty string into the [XML] column.
What am I not getting?
Clarification
I am not trying to get the inner text, subelements or attributes from each row using value(...). I am trying to capture the entire <row> element and save it to a column of type xml
I've experimented with nodes(...) and come up with:
INSERT into MyTable([XML])
SELECT C.query('*')
FROM #XML.nodes('rows/row') T(C)
Which is closer to what I want but the results don't include the outer <row> tag, just the <column*> elements it contains.
You need to use .nodes(...) to project each <row .../> as a row and the extract the attributes of interest using .value(...). Something like:
insert into MyTable(XML)
select x.value('text()', 'nvarchar(max)') as XML
from #XML.nodes(N'/rows/row') t(x);
text() will select the inner text of each <row .../>. You should use the appropriate expression (eg. node() or #attribute etc, see XPath examples), depending on what you want from the row (your example does not make it clear at all, with all those empty elements...).
T-SQL Script:
SET ANSI_WARNINGS ON;
DECLARE #x XML =
'<rows>
<row Atr1="11" />
<row Atr1="22" Atr2="B" />
<row Atr1="33" Atr2="C" />
</rows>';
DECLARE #Table TABLE(Id INT NOT NULL, [XMLColumn] XML NOT NULL);
INSERT #Table (Id, XMLColumn)
SELECT ROW_NUMBER() OVER(ORDER BY ##SPID) AS Id,
a.b.query('.') AS [XML]
FROM #x.nodes('//rows/row') AS a(b);
SELECT *
FROM #Table t;
Results:
Id XMLColumn
-- --------------------------
1 <row Atr1="11" />
2 <row Atr1="22" Atr2="B" />
3 <row Atr1="33" Atr2="C" />