'Hi, i am trying to create automated XML load into SQL and initially I need to create the table with right data set. Ultimately i am trying to get Serial Number, IP Address and Timezone with all count values for "From to "to" time stamp Here is how my XML looks like:
<response xmlns="http://www.test.com/sensor-api/v2">
<sensor-time timezone="America/New_York">2017-07-18T15:45:03-04:00</sensor-time>
<status>
<code>OK</code>
</status>
<sensor-info>
<serial-number>Q8:80:39:60:9Z:F2</serial-number>
<ip-address>192.167.130.18</ip-address>
<name>Test</name>
<group />
<device-type>PC2 - UL</device-type>
</sensor-info>
<content>
<elements>
<element>
<element-id>2</element-id>
<element-name>Conf_Lower_Zone</element-name>
<sensor-type>SINGLE_SENSOR</sensor-type>
<data-type>ZONE</data-type>
<from>2017-07-18T15:40:00-04:00</from>
<to>2017-07-18T15:45:00-04:00</to>
<resolution>ONE_MINUTE</resolution>
<measurements>
<measurement>
<from>2017-07-18T15:40:00-04:00</from>
<to>2017-07-18T15:41:00-04:00</to>
<values>
<value label="count">0</value>
</values>
</measurement>
<measurement>
<from>2017-07-18T15:41:00-04:00</from>
<to>2017-07-18T15:42:00-04:00</to>
<values>
<value label="count">0</value>
</values>
</measurement>
And Here is my SQL Part:
CREATE DATABASE OPENXMLTesting
GO
USE OPENXMLTesting
GO
CREATE TABLE XMLwithOpenXML
(
Id INT IDENTITY PRIMARY KEY,
XMLData XML,
LoadedDateTime DATETIME
)
INSERT INTO XMLwithOpenXML(XMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK 'C:\Example\Test.xml', SINGLE_BLOB) AS x;
SELECT * FROM XMLwithOpenXML
USE OPENXMLTesting
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT [element-id], [element-name], [data-type], [ip-address]
FROM OPENXML(#hDoc,
'content/elements/element/measurements/measurement/values/value')
WITH
(
[element-id] varchar (100) '#ElementID',
[element-name] varchar(50) '#Element_Name',
[data-type] varchar(50) '#Data_Type',
[ip-address] varchar(50) '#IP_Address'
)
EXEC sp_xml_removedocument #hDoc
GO
The problem is that as results i am getting only headers without any data under it. See the pic below.
What am i doing wrong?
There are two issues...
First of all there is a namespace involved. As it is the default namespace you have to declare or wildcard it.
Secondly the chosen approach with FROM OPENXML together with the two stored procedures to create and to remove a document is completely outdated. You should use the modern native XML methods. Try this:
DECLARE #xml XML=
N'<response xmlns="http://www.test.com/sensor-api/v2">
<sensor-time timezone="America/New_York">2017-07-18T15:45:03-04:00</sensor-time>
<status>
<code>OK</code>
</status>
<sensor-info>
<serial-number>Q8:80:39:60:9Z:F2</serial-number>
<ip-address>192.167.130.18</ip-address>
<name>Test</name>
<group />
<device-type>PC2 - UL</device-type>
</sensor-info>
<content>
<elements>
<element>
<element-id>2</element-id>
<element-name>Conf_Lower_Zone</element-name>
<sensor-type>SINGLE_SENSOR</sensor-type>
<data-type>ZONE</data-type>
<from>2017-07-18T15:40:00-04:00</from>
<to>2017-07-18T15:45:00-04:00</to>
<resolution>ONE_MINUTE</resolution>
<measurements>
<measurement>
<from>2017-07-18T15:40:00-04:00</from>
<to>2017-07-18T15:41:00-04:00</to>
<values>
<value label="count">0</value>
</values>
</measurement>
<measurement>
<from>2017-07-18T15:41:00-04:00</from>
<to>2017-07-18T15:42:00-04:00</to>
<values>
<value label="count">0</value>
</values>
</measurement>
</measurements>
</element>
</elements>
</content>
</response>';
--The query needs to declare the namespace
--I'll use direct calls to get the singular values
--...and .nodes() to get a derived set of 1:n related elements
WITH XMLNAMESPACES(DEFAULT 'http://www.test.com/sensor-api/v2')
SELECT #xml.value('(/response/sensor-time/#timezone)[1]','nvarchar(max)') AS SensortTime_TimeZone
,#xml.value('(/response/sensor-time/text())[1]','datetime') AS SensortTime
,#xml.value('(/response/status/code/text())[1]','nvarchar(max)') AS StatusCode
,#xml.value('(/response/sensor-info/serial-number/text())[1]','nvarchar(max)') AS SerialNumber
,e.value('(element-id/text())[1]','int') AS ElementId
,e.value('(element-name/text())[1]','nvarchar(max)') AS ElementName
--more from here
,m.value('(from/text())[1]','datetime') AS Measurement_From
,m.value('(to/text())[1]','datetime') AS Measurement_To
,m.value('(values/value/#label)[1]','nvarchar(max)') AS Measurement_Label
,m.value('(values/value/text())[1]','int') AS Measurement_Label
FROM #xml.nodes('/response/content/elements/element') A(e)
OUTER APPLY e.nodes('measurements/measurement') B(m);
Related
I'm trying to extract an XML value from a SQL Server column and put it into a SQL statement. My problem is that the documentation I've found doesn't explain how to do this if there are spaces or "" in the XML path.
I'm trying to extract the value property in the XML shown here (there is no namespace in the XML). The SQL Server column is called Settings:
<properties>
<settings hwid="stream:0.0.0">
<setting typeid="78622C19-58AE-40D4-8EEA-17351F4273B6">
<name>Codec</name>
<value>4</value>
</setting>
</settings>
</properties>
You can use OPENXML to retrieve data from xml, first create procedure like this:
CREATE PROCEDURE GetXmlValueProc
#xml NVARCHAR(max)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #hdoc INT;
EXEC sp_xml_preparedocument #hdoc OUTPUT, #xml;
DECLARE #Result NVARCHAR(50);
SELECT value
FROM
OPENXML(#hdoc, '/properties/settings/setting', 2)
WITH
(
value VARCHAR(100)
);
EXEC sp_xml_removedocument #hdoc;
END
GO
And call procedure in this way:
DECLARE #xml NVARCHAR(MAX)='<properties><settings hwid="stream:0.0.0"><setting typeid="78622C19-58AE-40D4-8EEA-17351F4273B6"><name>Codec</name><value>4</value></setting></settings></properties>'
EXEC dbo.GetXmlValueProc #xml
Even you can make procedure more generic and pass the xml path to get data.
I don't see any spaces in your XML. If you mean the various attributes, such as hwid, those are parsed separately from the node names. You can select those by prefacing with #.
I assume the type of the value node is int, if not you can change it below:
SELECT
t.Settings.value('(/properties/settings/setting/value)[1]', 'int'),
t.Settings.value('(/properties/settings/setting/#typeid)[1]', 'uniqueidentifier'),
t.Settings.value('(/properties/settings/#hwid)[1]', 'nvarchar(max)')
FROM myTable t
For reference, if you ever did have a node with a space in it: it would be encoded and a double-quote as "
I've read all of the other posts on this subject but none are working for me. This one is the closest CONVERTING SQL NVARCHAR(MAX) TO XML
So, I've modified that code to try and fit my situation. I have a nvarchar(max) column that contains XML data as one big long string. It's not my database, so I can't change the datatype to XML. The next best thing is to create a query and cast it as XML. Here is my code:
IF OBJECT_ID('tempdb..#XML_Dummy') IS NOT NULL DROP TABLE #XML_Dummy
GO
CREATE TABLE #XML_Dummy
(
[ID] [INT] IDENTITY(1, 1)
NOT NULL ,
[XMLValue] [NVARCHAR](MAX) NULL
);
GO
INSERT INTO #XML_Dummy
(
[XMLValue]
)
select top 10 xmlcol from MyTable
GO
SELECT
b.x.value('(/Proponix/Header/SubHeader/InstrumentID)[1]', 'varchar(max)')
FROM
#XML_Dummy a
CROSS APPLY (
SELECT
CAST(CAST ([XMLValue] AS VARCHAR(MAX)) AS XML) x
) b;
IF OBJECT_ID('tempdb..#XML_Dummy') IS NOT NULL DROP TABLE #XML_Dummy;
I'm not sure why I'm getting all NULLS back. I want to return the InstrumentID. Here is an excerpt of my XML:
<?xml version="1.0"?>
<Proponix>
<Header>
Header stuff
</Header>
<SubHeader>
<InstrumentID>BS6000000001</InstrumentID>
</SubHeader>
A lot more fields
What am I missing? This is my first time working with XML in SQL server.
SubHeader is not a child of Header in your example. Try:
b.x.value('(/Proponix/SubHeader/InstrumentID)[1]', 'varchar(max)')
You try to find the SubHeader as a child of Header but it is a sibling...
Try this:
EDIT: changed #x to simulate a table with a nvarchar-column containing different xml_msg values:
EDIT2: adapted according to calls the OP showed in chat
WITH ConvertedToXML AS
(
SELECT xml_msg AsVarchar
,CAST(xml_msg AS XML) AS AsXml
FROM myTable
)
SELECT ConvertedToXML.AsVarchar
,ConvertedToXML.AsXml
,ConvertedToXML.AsXml.value('(/Proponix/SubHeader/InstrumentID)[1]', 'varchar(max)') AS InstrumentID
FROM ConvertedToXML
--old Text
declare #x table(xml_msg nvarchar(max));
insert into #x VALUES
('<?xml version="1.0"?>
<Proponix>
<Header>
Header stuff
</Header>
<SubHeader>
<InstrumentID>BS6000000001</InstrumentID>
</SubHeader>
</Proponix>')
,('<?xml version="1.0"?>
<Proponix>
<Header>
Header stuff
</Header>
<SubHeader>
<InstrumentID>BS6000000002</InstrumentID>
</SubHeader>
</Proponix>')
,('<?xml version="1.0"?>
<Proponix>
<Header>
Header stuff
</Header>
<SubHeader>
<InstrumentID>BS6000000003</InstrumentID>
</SubHeader>
</Proponix>');
WITH ConvertedToXML AS
(
SELECT TOP 10 CAST(xml_msg AS XML) AS AsXml FROM #x
)
SELECT
ConvertedToXML.AsXml.value('(/Proponix/SubHeader/InstrumentID)[1]', 'varchar(max)')
FROM ConvertedToXML
Please help. After reading all google and stackoverflow :) my brain does not work anymore.
I have below TSQL (running on SQL Server 2012)
I cannot figure out where I need to declare my a: namespace? And how many namespaces I need to declare?
DECLARE #XML AS XML
DECLARE #hDoc AS INT
SELECT #XML = '<GetAssetWarrantyResponse xmlns="http://tempuri.org/">
<GetAssetWarrantyResult xmlns:a="http://schemas.datacontract.org/2004/07/Dell.AWR.Domain.Asset" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Faults />
<a:Response>
<a:DellAsset>
<a:AssetParts i:nil="true" />
<a:CountryLookupCode>5252</a:CountryLookupCode>
<a:CustomerNumber>645651</a:CustomerNumber>
</a:DellAsset>
</a:Response>
</GetAssetWarrantyResult>
</GetAssetWarrantyResponse>'
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT CountryLookupCode
FROM OPENXML(#hDoc, 'GetAssetWarrantyResponse/GetAssetWarrantyResult/a:Response/a:DellAsset')
WITH
(
CountryLookupCode [nvarchar](20) 'a:CountryLookupCode'
)
EXEC sp_xml_removedocument #hDoc
GO
You need to specify the namespace prefixes when invoking sp_xml_preparedocument. In this case you have the a namespace and the default namespace (the one without prefix: xmlns="....") :
EXEC sp_xml_preparedocument
#hDoc OUTPUT
, #XML
, '<root xmlns:d="http://tempuri.org/" xmlns:a="http://schemas.datacontract.org/2004/07/Dell.AWR.Domain.Asset"/>'
And use the registered prefixes properly :
SELECT CountryLookupCode
FROM OPENXML(#hDoc, 'd:GetAssetWarrantyResponse/d:GetAssetWarrantyResult/a:Response/a:DellAsset')
WITH
(
CountryLookupCode [nvarchar](20) 'a:CountryLookupCode'
)
I'd use the built-in, native XQuery support in SQL Server:
DECLARE #XML AS XML
SELECT #XML = '<GetAssetWarrantyResponse xmlns="http://tempuri.org/">
<GetAssetWarrantyResult xmlns:a="http://schemas.datacontract.org/2004/07/Dell.AWR.Domain.Asset" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Faults />
<a:Response>
<a:DellAsset>
<a:AssetParts i:nil="true" />
<a:CountryLookupCode>5252</a:CountryLookupCode>
<a:CustomerNumber>645651</a:CustomerNumber>
</a:DellAsset>
</a:Response>
</GetAssetWarrantyResult>
</GetAssetWarrantyResponse>'
;WITH XMLNAMESPACES(DEFAULT 'http://tempuri.org/', 'http://schemas.datacontract.org/2004/07/Dell.AWR.Domain.Asset' AS ns)
SELECT
#XML.value('(GetAssetWarrantyResponse/GetAssetWarrantyResult/ns:Response/ns:DellAsset/ns:CountryLookupCode)[1]', 'int')
In terms of XML namespaces: you need to define all those used in your XML document up to the point where you want to get the data from
I need help parsing this XML in TSQL:
<Settings>
<Setting ERName="CAPTURE_MODE_ID" Value="9" />
<Setting ERName="VEHICLE_TYPE" Value="7" />
</Settings>
I would like to return the values as such:
Capture_Mode_Id Vehicle_Type
9 7
SQL Fiddle
declare #XML xml
set #XML = '
<Settings>
<Setting ERName="CAPTURE_MODE_ID" Value="9" />
<Setting ERName="VEHICLE_TYPE" Value="7" />
</Settings>'
select #XML.value('(/Settings/Setting[#ERName = "CAPTURE_MODE_ID"]/#Value)[1]', 'int') as CAPTURE_MODE_ID,
#XML.value('(/Settings/Setting[#ERName = "VEHICLE_TYPE"]/#Value)[1]', 'int') as VEHICLE_TYPE
Results:
| CAPTURE_MODE_ID | VEHICLE_TYPE |
|-----------------|--------------|
| 9 | 7 |
You need to perform two logical steps as best I can tell:
Parse the XML - i.e. get a result set from it.
Pivot the result set.
To get a result set from the XML, use the OPENXML function - something like the following...
DECLARE #idoc int, #doc varchar(1000);
-- the XML document
SET #doc ='
<ROOT>
<Settings>
<Setting ERName="CAPTURE_MODE_ID" Value="9" />
<Setting ERName="VEHICLE_TYPE" Value="7" />
</Settings>
</ROOT>';
-- Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc;
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/ROOT/Settings/Setting', 1)
WITH (ERName varchar(20), Value varchar(20));
..., which yields the following results:
ERName Value
--------------- -----
CAPTURE_MODE_ID 9
VEHICLE_TYPE 7
To pivot the result set without aggregation, consider the ideas presented to this end in a CALSQL blog post.
I have a large XML note with many nodes.
is there a way that I can select only a single node and all of its contents from the larger XML?
i am using sql 2005
You should use the query() Method if you want to get a part of your XML.
declare #XML xml
set #XML =
'
<root>
<row1>
<value>1</value>
</row1>
<row2>
<value>2</value>
</row2>
</root>
'
select #XML.query('/root/row2')
Result:
<row2>
<value>2</value>
</row2>
If you want the value from a specific node you should use value() Method.
select #XML.value('(/root/row2/value)[1]', 'int')
Result:
2
Update:
If you want to shred your XML to multiple rows you use nodes() Method.
To get values:
declare #XML xml
set #XML =
'
<root>
<row>
<value>1</value>
</row>
<row>
<value>2</value>
</row>
</root>
'
select T.N.value('value[1]', 'int')
from #XML.nodes('/root/row') as T(N)
Result:
(No column name)
1
2
To get the entire XML:
select T.N.query('.')
from #XML.nodes('/root/row') as T(N)
Result:
(No column name)
<row><value>1</value></row>
<row><value>2</value></row>