Pull out Attributes from TSQL XML - sql

I have the following XML:
<help>
<item field="field1" help="helptext" />
<item field="field2" help="helptext" />
<item field="field3" help="helptext" />
</help>
I have this stored in an XML DataType in SQL server and would like to pull out the fields and help text seperately. I.e a results set similar to:
| field1 | helptext |
| field2 | helptext |
| field3 | helptext |
Does anybody know how to achieve this?
Kindest Regards,
Adam

DECLARE #xml AS XML = '<help>
<item field="field1" help="helptext" />
<item field="field2" help="helptext" />
<item field="field3" help="helptext" />
</help>'
select c.value('#field', 'varchar(max)') field, c.value('#help', 'varchar(max)') help
from #xml.nodes('/help/item') T(c);
To achieve the same when xml data is in a table field use:
SELECT c.value('#field', 'varchar(max)') field, c.value('#help', 'varchar(max)') help
FROM MyTable A CROSS APPLY A.MyXmlField.nodes('/help/item') T(c)
Where MyTable is the table with field MyXmlField containing xml data.

See if following helps:
DECLARE #processedXmlDoc int
DECLARE #inputDoc varchar(1000)
EXEC sp_xml_preparedocument #processedXmlDoc OUTPUT, #inputDoc
SELECT *
FROM OPENXML (#processedXmlDoc , '/help/item',1)
WITH (field varchar(20) 'field',
help varchar(20) 'helpText')
EXECUTE sp_xml_removedocument #idoc
Hope that helps.

Try something like this:
DECLARE #input XML = '<help>
<item field="field1" help="helptext" />
<item field="field2" help="helptext" />
<item field="field3" help="helptext" />
</help>'
SELECT
Field = Item.value('#field', 'varchar(25)'),
HELP = Item.value('#help', 'varchar(50)')
FROM
#input.nodes('/help/item') AS XTbl(Item)

Related

OpenXML returning NULL

I am trying to import xml into my database with the following query using OpenXML in Microsoft SQL Server:
DECLARE #xml XML;
DECLARE #y INT;
SET #xml
= '<ArrayOfArticle xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Article>
<ScriptId xmlns="https://test.com/">5135399</ScriptId>
<Title xmlns="https://test.com/">Stocks divided into two corners</Title>
<Mediatype xmlns="https://test.com/">News papeer</Mediatype>
<Abstract xmlns="https://test.com/">Foreign capital doubled this year.</Abstract>
<ScriptDate xmlns="https://test.com/">2017-12-30T00:00:00</ScriptDate>
<ScriptTypeId xmlns="https://test.com/">1</ScriptTypeId>
<ScriptType xmlns="https://test.com/">News general</ScriptType>
<Media xmlns="https://test.com/">Times</Media>
<ArticleUrl xmlns="https://test.com/">http://test.com</ArticleUrl>
<AnalysisResult xmlns="https://test.com/">
<Analysis>
<Regno>111</Regno>
<Name>New York Times</Name>
<Result>1</Result>
<ResultName>Positive</ResultName>
</Analysis>
<Analysis>
<Regno>222</Regno>
<Name>Washington Post</Name>
<Result>1</Result>
<ResultName>Negative</ResultName>
</Analysis>
</AnalysisResult>
<FacebookStats xmlns="https://test.com/">
<ShareCount xsi:nil="true" />
<LikeCount xsi:nil="true" />
<CommentCount xsi:nil="true" />
<TotalCount xsi:nil="true" />
</FacebookStats>
<MediaScore xmlns="https://test.com/">
<MediaScore>
<Regno>111</Regno>
<CompanyName>New York Times</CompanyName>
<MediaScoreID>2</MediaScoreID>
<Name>Neither</Name>
</MediaScore>
<MediaScore>
<Regno>222</Regno>
<CompanyName>Washington Post</CompanyName>
<MediaScoreID>2</MediaScoreID>
<Name>Neither</Name>
</MediaScore>
</MediaScore>
<Page xmlns="https://test.com/">26</Page>
<ProgramId xmlns="https://test.com/">0</ProgramId>
<ProgramTime xmlns="https://test.com/" xsi:nil="true" />
<ProgramLength xmlns="https://test.com/">0</ProgramLength>
<ProgramOrder xmlns="https://test.com/">0</ProgramOrder>
</Article>
</ArrayOfArticle>';
EXEC sp_xml_preparedocument #y OUTPUT, #xml;
SELECT *
FROM
OPENXML(#y, '/ArrayOfArticle/Article', 1)
WITH
(
ScriptId VARCHAR(20),
Title VARCHAR(30),
Mediatype VARCHAR(30)
);
The query however only returns NULL values. What am I missing here? Would it be optimal to import the XML using SSIS instead. Not sure how much more details I can give at the given hour.
Do not use FROM OPENXML. This approach (together with the corresponding SPs to prepare and to remove a document) is outdated and should not be used any more.
Try the XML type's native methods, in this case .value():
Your XML is rather weird - concerning namespaces. If its creation is under your control you should try to clean this namespace mess. The unusual thing is, that your XML declares default namespaces over and over.
You can use the deep search with // together with a namespace wildcard *:
--GetItEasyCheesy (not recommended)
SELECT #xml.value(N'(//*:ScriptId)[1]',N'int') AS ScriptId
,#xml.value(N'(//*:Title)[1]',N'nvarchar(max)') AS Title
,#xml.value(N'(//*:Mediatype )[1]',N'nvarchar(max)') AS Mediatype ;
You can declare the namespace as default, but in this case you must wildcard the outer elements, as they are not part of this namespace:
--Use a default namespace
WITH XMLNAMESPACES(DEFAULT 'https://test.com/')
SELECT #xml.value(N'(/*:ArrayOfArticle/*:Article/ScriptId/text())[1]',N'int') AS ScriptId
,#xml.value(N'(/*:ArrayOfArticle/*:Article/Title/text())[1]',N'nvarchar(max)') AS Title
,#xml.value(N'(/*:ArrayOfArticle/*:Article/Mediatype/text())[1]',N'nvarchar(max)') AS Mediatype;
The recommended approach is to bind the inner namespace to a prefix and use this
--Recommended
WITH XMLNAMESPACES('https://test.com/' AS ns)
SELECT #xml.value(N'(/ArrayOfArticle/Article/ns:ScriptId/text())[1]',N'int') AS ScriptId
,#xml.value(N'(/ArrayOfArticle/Article/ns:Title/text())[1]',N'nvarchar(max)') AS Title
,#xml.value(N'(/ArrayOfArticle/Article/ns:Mediatype/text())[1]',N'nvarchar(max)') AS Mediatype;
If your <ArrayOfArticles> contains more than one <Article> you can use .nodes() to get alle of them as derived table. In this case the query is
WITH XMLNAMESPACES('https://test.com/' AS ns)
SELECT art.value(N'(ns:ScriptId/text())[1]',N'int') AS Recommended
,art.value(N'(ns:Title/text())[1]',N'nvarchar(max)') AS Title
,art.value(N'(ns:Mediatype/text())[1]',N'nvarchar(max)') AS Mediatype
FROM #xml.nodes(N'/ArrayOfArticle/Article') AS A(art);
your XML contains namespaces, I'd use xquery in order to extract the data from your XML
UPDATE with additional elements extract
DECLARE #xml XML;
SET #xml
= '<ArrayOfArticle xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Article>
<ScriptId xmlns="https://test.com/">5135399</ScriptId>
<Title xmlns="https://test.com/">Stocks divided into two corners</Title>
<Mediatype xmlns="https://test.com/">News papeer</Mediatype>
<Abstract xmlns="https://test.com/">Foreign capital doubled this year.</Abstract>
<ScriptDate xmlns="https://test.com/">2017-12-30T00:00:00</ScriptDate>
<ScriptTypeId xmlns="https://test.com/">1</ScriptTypeId>
<ScriptType xmlns="https://test.com/">News general</ScriptType>
<Media xmlns="https://test.com/">Times</Media>
<ArticleUrl xmlns="https://test.com/">http://test.com</ArticleUrl>
<AnalysisResult xmlns="https://test.com/">
<Analysis>
<Regno>111</Regno>
<Name>New York Times</Name>
<Result>1</Result>
<ResultName>Positive</ResultName>
</Analysis>
<Analysis>
<Regno>222</Regno>
<Name>Washington Post</Name>
<Result>1</Result>
<ResultName>Negative</ResultName>
</Analysis>
</AnalysisResult>
<FacebookStats xmlns="https://test.com/">
<ShareCount xsi:nil="true" />
<LikeCount xsi:nil="true" />
<CommentCount xsi:nil="true" />
<TotalCount xsi:nil="true" />
</FacebookStats>
<MediaScore xmlns="https://test.com/">
<MediaScore>
<Regno>111</Regno>
<CompanyName>New York Times</CompanyName>
<MediaScoreID>2</MediaScoreID>
<Name>Neither</Name>
</MediaScore>
<MediaScore>
<Regno>222</Regno>
<CompanyName>Washington Post</CompanyName>
<MediaScoreID>2</MediaScoreID>
<Name>Neither</Name>
</MediaScore>
</MediaScore>
<Page xmlns="https://test.com/">26</Page>
<ProgramId xmlns="https://test.com/">0</ProgramId>
<ProgramTime xmlns="https://test.com/" xsi:nil="true" />
<ProgramLength xmlns="https://test.com/">0</ProgramLength>
<ProgramOrder xmlns="https://test.com/">0</ProgramOrder>
</Article>
</ArrayOfArticle>'
DECLARE #T TABLE (XmlCol XML)
INSERT INTO #T
SELECT #xml
;WITH XMLNAMESPACES ('https://test.com/' as p1)
SELECT z.t.value ('../../p1:ScriptId[1]',' varchar(100)') ScriptId,
z.t.value ('../../p1:Title[1]',' varchar(100)') Title,
z.t.value ('../../p1:Mediatype[1]',' varchar(100)') Mediatype,
z.t.value ('p1:CompanyName[1]', 'varchar(100)') CompanyName
FROM #T t
CROSS APPLY XmlCol.nodes ('/ArrayOfArticle/Article/p1:MediaScore/p1:MediaScore') z(t)
DECLARE #y INT
EXEC sp_xml_preparedocument #y OUTPUT, #xml,
'<ns xmlns:x="https://test.com/"/>'
SELECT *
FROM
OPENXML(#y, '/ArrayOfArticle/Article', 2)
WITH
(
[ScriptId] VARCHAR(20) 'x:ScriptId', --<< and so on
[Title] VARCHAR(30),
Mediatype VARCHAR(30)
)
EXEC sp_xml_removedocument #y --<< lost in your code

Querying XML data in SQL Server 2012

I have a database that has a table Parameters_XML with columns.
id, application, parameter_nr flag, value
The parameter_nr is for example 1 and the value for that parameter is the following:
<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>
I need to retrieve the values for all the applications where the item is 341, 342 and 2196.
Eg: for the application 1 the value for the item 341 is 2 and so on.
I have written the following query:
SELECT cast (value as XML).value('data(/Root/Row/Item[#id="431"],')
FROM Parameters_Xml x
WHERE parameter_nr = 1
I get the following error:
Msg 174, Level 15, State 1, Line 1
The value function requires 2 argument(s).
Why my query is not valid?
Try someting like this:
SELECT
CAST(x.Value AS XML).value('(/Root/Row/Item[#id="341"]/Str)[1]', 'nvarchar(100)')
FROM dbo.Parameters_Xml x
WHERE parameter_nr = 1
You're telling SQL Server to go find the <Item> node (under <Root> / <Row>) with and id=341 (that what I'm assuming - your value in the question doesn't even exist) and then get the first <Str> node under <Item> and return that value
Also: why do you need CAST(x.Value as XML) - if that column contains only XML - why isn't it defined with datatype XML to begin with? If you have this, you don't need any CAST ...
DECLARE #str XML;
SET #str = '<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>'
-- if you want specific values then
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #str.nodes('//Root/Row/Item') xmlData(Col)
where xmlData.Col.value('#id','varchar(max)') = 342
--if you want all values then
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #str.nodes('//Root/Row/Item') xmlData(Col)
--where xmlData.Col.value('#id','varchar(max)') = 342
Edit After CommentIf i query my db: select * from parameters_xml where parameter_nr = 1 i will receive over 10000 rows, each row is like the following: Id app param value 1 1 1 11 I need for all the 10000 apps to retrieve the item id and the value from the XML value - like you did for my eg.
-- declare temp table
declare #temp table
(val xml)
insert into #temp values ('<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>')
insert into #temp values ('<Root>
<Row>
<Item id="3411" flags="1">
<Str>21</Str>
</Item>
<Item id="3421" flags="1">
<Str>101</Str>
</Item>
<Item id="21961" flags="1">
<Str>7REPJ11</Str>
</Item>
</Row>
</Root>')
-- QUERY
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #temp AS T
outer apply T.val.nodes('/Root/Row/Item') as xmlData(Col)
Try this:
select cast (value as
XML).value('data(/Root/Row/Item[#id="431"]','nvarchar(max)')
data type is 2nd parameter.

Parsing XML using TSQL

I'm trying to parse out the following XML with TSQL:
<Response xmlns="http://data.fcc.gov/api" status="OK" executionTime="9">
<Block FIPS="181770103002004" />
<County FIPS="18177" name="Wayne" />
<State FIPS="18" code="IN" name="Indiana" />
</Response>
Using the following script:
SELECT x.i.value('#name', 'varchar(200)') AS county
FROM #xml.nodes('Response/County') AS x(i)
But I get no results, any help as to what I'm doing wrong would be greatly appreciated.
Thanks!
Your XML namespace is messing things up. Either remove the xmlns="http://data.fcc.gov/api" from the Response element, or prefix your query with WITH XMLNAMESPACES ( DEFAULT 'http://data.fcc.gov/api')
;WITH XMLNAMESPACES ( DEFAULT 'http://data.fcc.gov/api')
SELECT x.i.value('#name', 'varchar(200)') AS county
FROM #xml.nodes('Response/County') AS x(i)
Or you can use wildcard namespaces in the query:
SELECT x.i.value('#name', 'varchar(200)') AS county
FROM #xml.nodes('*:Response/*:County') AS x(i)
You can do it using OPENXML like this:
DECLARE #idoc INT
DECLARE #xml AS XML =
'<Response xmlns="http://data.fcc.gov/api" status="OK" executionTime="9">
<Block FIPS="181770103002004" />
<County FIPS="18177" name="Wayne" />
<State FIPS="18" code="IN" name="Indiana" />
</Response>'
EXEC sp_xml_preparedocument #idoc OUTPUT, #xml, N'<root xmlns:n="http://data.fcc.gov/api" />'
SELECT
Name AS County
FROM OPENXML (#idoc, '/n:Response/n:County', 1)
WITH
(
Name VARCHAR(255) '#name'
)
EXEC sp_xml_removedocument #idoc
GO

Processing XML Hierarchy with MS SQL

How would I specify the following XML Hierarchy into readable columns in Microsoft SQL?
<transaction id=1>
<item id=1>
<price>1</price>
</item>
<item id=2>
<price>1</price>
</item>
</transaction>
<transaction>
<item id=1>
<price>1</price>
</item>
</transaction>
for instance
select
x.i.value('(????)','Varchar(max)') [TransId]
x.i.value('(????)','Varchar(max)') [ItemId]
x.i.value('(????)','Varchar(max)') [PriceId]
from #xml.nodes('/transaction') x(i)
Thanks in advance.
Attribute values must always appear in quotation marks in XML.
Not sure about the desired output. An example would be:
declare #xml xml
Select #xml=
'<transaction id="1">
<item id="1">
<price>1</price>
</item>
<item id="2">
<price>2</price>
</item>
</transaction>
<transaction>
<item id="1">
<price>3</price>
</item>
</transaction>'
SELECT
y.value('../#id','int') as TransactionID,
y.value('#id','int') as ItemID,
y.value('(./price/text())[1]', 'Varchar(max)') as Price
FROM #xml.nodes('/transaction/item') as x(y)
order by TransactionID,ItemID
with the output:
NULL 1 3
1 1 1
1 2 2
Actually usually it's faster to shred XML from parent to child using apply, like this:
select
t.c.value('#id','int') as TransId,
i.c.value('#id','int') as ItemId,
i.c.value('(price/text())[1]', 'int') as PriceId
from #xml.nodes('transaction') as t(c)
outer apply t.c.nodes('item') as i(c)
order by TransId, ItemID
sql fiddle demo

SQL Query which deletes an attribute in XML

I have column of XML type.There's a xml like this
<items>
<item type="xxx"><items>
<item type="xxx"><items>
</items>
I need to delete all type attributes. I know oracle has some functions for xml manipulation, but I don't get how to delete attributes.
How would such query look like ?
Here is an example using the Oracle supplied SQL function deletexml
Acknowledgement to Jonas Lincoln as I am using his XPATH expression
SELECT deleteXML(xmltype.CREATEXML('<items>
<item type="xxx">a</item>
<item type="xxx">b</item>
</items>'),
'/items/item[#type="xxx"]/#type')
FROM dual
<items>
<item>a</item>
<item>b</item>
</items>
declare #xml as xml
set #xml = '
<items>
<item type="xxx">3</item>
<item type="xxx">4</item>
</items>'
SET #xml.modify('delete (/items/item[#type="xxx"]/#type)')
select cast(#xml as nvarchar(100))
<items>
<item>3</item>
<item>4</item>
</items>