How to read attributes in a XML file using nodes in SQL - sql

I am trying to read ALL the Items from this XML using nodes but I jus manage to return just very first one, or none with the following examples.
How to read the entire list?
The file can contain hundreds of thousands.
XML file
<data source="1" target="0">
<list item_nb="1">
<co id="1" constitution="20190612101007" item_nb="44046">
<item cm="640001000101" obu="00007E" vc="2" vrn="SOLD" />
<item cm="640001000101" obu="00009D" vc="6" vrn="04D11797" />
<item cm="640001000101" obu="0000A3" vc="2" vrn="FAULTY" />
<item cm="640001000101" obu="00018B" vc="2" vrn="07D54084" />
<item cm="640001000101" obu="0001A6" vc="6" vrn="000422" />
<item cm="640001000101" obu="0001B2" vc="2" vrn="90D24430" />
<item cm="640001000101" obu="0001B3" vc="2" vrn="03LS1592" />
<item cm="640001000101" obu="0001B9" vc="6" vrn="FAULTYJUNE15" />
</co>
</list>
</data>
My Code in SQL (return only the first item)
declare #X xml;
select #X = T.MY_XML
from openrowset(bulk 'C:\XML\IEA.1.20190612101007-WL.XML', single_blob) as T(MY_XML)
select
MY_XML.Item.value('(item/#cm)[1]', 'VARCHAR(20)'),
MY_XML.Item.value('(item/#obu)[1]', 'VARCHAR(50)'),
MY_XML.Item.value('(item/#vc)[1]', 'VARCHAR(50)'),
MY_XML.Item.value('(item/#vrn)[1]', 'VARCHAR(50)')
from #X.nodes('data/list/co') AS MY_XML (Item);

found a way to work!
declare #X xml;
select #X = T.MY_XML
from openrowset(bulk 'C:\XML\IEA.1.20190612101007-WL.XML', single_blob) as T(MY_XML)
select
MY_XML.Item.value('(#cm)[1]', 'VARCHAR(20)'),
MY_XML.Item.value('(#obu)[1]', 'VARCHAR(50)'),
MY_XML.Item.value('(#vc)[1]', 'VARCHAR(50)'),
MY_XML.Item.value('(#vrn)[1]', 'VARCHAR(50)')
from #X.nodes('data/list/co/item') AS MY_XML (Item);

Related

Import XML to SQL

Sorry to put one more post regarding this topic, but I am desperate trying to import this xml to the database without any success.
This is the xml:
<session xmlns="http://winscp.net/schema/session/1.0" start="2014-11-03T17:23:22.376Z">
<ls>
<destination value="/Output" />
<files>
<file>
<filename value="." />
<type value="d" />
<modification value="2014-11-03T12:17:58.000Z" />
<permissions value="rwxr-sr-x" />
</file>
<file>
<filename value="7215_SG.csv" />
<type value="-" />
<size value="1584161" />
<modification value="2014-11-03T12:06:46.000Z" />
<permissions value="rw-r--r--" />
</file>
<file>
<filename value="6171_SG.csv" />
<type value="-" />
<size value="2298481" />
<modification value="2014-11-03T12:05:13.000Z" />
<permissions value="rw-r--r--" />
</file>
</files>
<result success="true" />
</ls>
</session>
And this is the sql statement to import the xml:
SELECT
xmldata.value('(./filename/#value)[1]', 'NVARCHAR(50)') AS szFilename,
xmldata.value('(./type/#value)[1]', 'NVARCHAR(50)') AS szType,
xmldata.value('(./size/#value)[1]', 'NVARCHAR(50)') AS szSize,
xmldata.value('(./modification/#value)[1]', 'NVARCHAR(50)') AS szModification,
xmldata.value('(./permissions/#value)[1]', 'NVARCHAR(50)') AS szPermissions
FROM
(SELECT CAST(x AS XML)
FROM OPENROWSET(BULK 'd:\temp\Test\log.xml',
SINGLE_BLOB) AS T(x)) AS T(x)
CROSS APPLY
x.nodes('//session/ls/files/file') AS X(xmldata);
Can you help me and tell me what am I doing wrong?
Thanks in advance!
You just simply need to respect the XML namespace that's defined on your XML root node!
<session xmlns="http://winscp.net/schema/session/1.0"
********************************************
To accommodate this XML namespace, you need to define in in your XQuery - best approach in my opinion is by using the WITH XMLNAMESPACES statement:
;WITH XMLNAMESPACES(DEFAULT 'http://winscp.net/schema/session/1.0')
SELECT
xmldata.value('(./filename/#value)[1]', 'NVARCHAR(50)') AS szFilename,
xmldata.value('(./type/#value)[1]', 'NVARCHAR(50)') AS szType,
xmldata.value('(./size/#value)[1]', 'NVARCHAR(50)') AS szSize,
xmldata.value('(./modification/#value)[1]', 'NVARCHAR(50)') AS szModification,
xmldata.value('(./permissions/#value)[1]', 'NVARCHAR(50)') AS szPermissions
FROM
(SELECT CAST(x AS XML)
FROM OPENROWSET(BULK 'd:\temp\Test\log.xml', SINGLE_BLOB) AS T(x)) AS T(x)
CROSS APPLY
x.nodes('//session/ls/files/file') AS X(xmldata);

Extracting data from XML using OpenXML in SQL Server

I have a xml which I want it to be extracted using OpenXML within SQL Server
Here is the sample XML
<row>
<student_token>7</student_token>
<student_ssn>552</student_ssn>
<alternate_id>20</alternate_id>
<old_ssn xsi:nil="true" />
<alien_num xsi:nil="true" />
<last_name>A</last_name>
<first_name>B</first_name>
<middle_init xsi:nil="true" />
<drivers_license_num xsi:nil="true" />
<gpa_highschool xsi:nil="true" />
<created_dt>2006-07-13T11:15:08.320</created_dt>
<created_how>4</created_how>
<modified_dt>2008-02-14T00:00:00</modified_dt>
<modified_by>4</modified_by>
<primary_street2 xsi:nil="true" />
<primary_street3 xsi:nil="true" />
<primary_country xsi:nil="true" />
<email_address xsi:nil="true" />
<address_start_dt xsi:nil="true" />
<address_end_dt xsi:nil="true" />
<entrance_iv_dt xsi:nil="true" />
<entrance_iv_by xsi:nil="true" />
<exit_iv_dt>2006-11-02T00:00:00</exit_iv_dt>
<exit_iv_by>156</exit_iv_by>
<foreign_address_indicator>N</foreign_address_indicator>
<foreign_postal_code xsi:nil="true" />
<pin>J27841</pin>
<web_id>J08614 </web_id>
<prior_name xsi:nil="true" />
<orig_eps xsi:nil="true" />
<web_role>STU1</web_role>
<heal_limit_flag>N</heal_limit_flag>
<email_address_2>test#test.com</email_address_2>
<cellular_telephone>415</cellular_telephone>
<alt_loan_debt xsi:nil="true" />
<web_last_login xsi:nil="true" />
<foreign_country_code xsi:nil="true" />
<entrance_iv_dt_grad_plus xsi:nil="true" />
<entrance_iv_by_grad_plus xsi:nil="true" />
<failed_logins>0</failed_logins>
<hispanic xsi:nil="true" />
<race xsi:nil="true" />
<primary_phone_number_intl xsi:nil="true" />
<security_version>0</security_version>
<failed_challenge_response>0</failed_challenge_response>
<require_pin_reset xsi:nil="true" />
</row>
The query should extract into 3 fields for each row
FieldName
FieldValue
IsNull
For example the first row should be
FieldName = student_token - The node name would be the field name
FieldValue = 7
IsNull = false - IsNull is based on the attribute xsi:nil="true"
How can I do this?
Sample data with namespace added.
declare #xml xml
set #xml =
'<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<student_token>7</student_token>
<student_ssn>552</student_ssn>
<alternate_id>20</alternate_id>
<old_ssn xsi:nil="true" />
<alien_num xsi:nil="true" />
<last_name>A</last_name>
<first_name>B</first_name>
<middle_init xsi:nil="true" />
<drivers_license_num xsi:nil="true" />
<gpa_highschool xsi:nil="true" />
<created_dt>2006-07-13T11:15:08.320</created_dt>
<created_how>4</created_how>
<modified_dt>2008-02-14T00:00:00</modified_dt>
<modified_by>4</modified_by>
<primary_street2 xsi:nil="true" />
<primary_street3 xsi:nil="true" />
<primary_country xsi:nil="true" />
<email_address xsi:nil="true" />
<address_start_dt xsi:nil="true" />
<address_end_dt xsi:nil="true" />
<entrance_iv_dt xsi:nil="true" />
<entrance_iv_by xsi:nil="true" />
<exit_iv_dt>2006-11-02T00:00:00</exit_iv_dt>
<exit_iv_by>156</exit_iv_by>
<foreign_address_indicator>N</foreign_address_indicator>
<foreign_postal_code xsi:nil="true" />
<pin>J27841</pin>
<web_id>J08614 </web_id>
<prior_name xsi:nil="true" />
<orig_eps xsi:nil="true" />
<web_role>STU1</web_role>
<heal_limit_flag>N</heal_limit_flag>
<email_address_2>test#test.com</email_address_2>
<cellular_telephone>415</cellular_telephone>
<alt_loan_debt xsi:nil="true" />
<web_last_login xsi:nil="true" />
<foreign_country_code xsi:nil="true" />
<entrance_iv_dt_grad_plus xsi:nil="true" />
<entrance_iv_by_grad_plus xsi:nil="true" />
<failed_logins>0</failed_logins>
<hispanic xsi:nil="true" />
<race xsi:nil="true" />
<primary_phone_number_intl xsi:nil="true" />
<security_version>0</security_version>
<failed_challenge_response>0</failed_challenge_response>
<require_pin_reset xsi:nil="true" />
</row>'
Using openxml.
declare #idoc int
exec sp_xml_preparedocument #idoc out, #xml, '<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>'
select FieldName,
FieldValue,
isnull([IsNull], 0)
from openxml(#idoc, '/row/*',1)
with (
FieldName varchar(50) '#mp:localname',
FieldValue varchar(50) '.',
[IsNull] bit '#xsi:nil'
)
exec sp_xml_removedocument #idoc
Using the XML data type:
;with xmlnamespaces('http://www.w3.org/2001/XMLSchema-instance' as ns)
select T.N.value('local-name(.)', 'varchar(50)') as FieldName,
T.N.value('.', 'varchar(50)') as FieldValue,
isnull(T.N.value('#ns:nil', 'bit'), 0) as [IsNull]
from #xml.nodes('/row/*') as T(N)
Not sure if you have that XML as a SQL variable or inside a table - question is very unclear .....
If you have it as an SQL variable, then try something like this (note: you must declare the xsi prefix somehow - otherwise SQL Server's XML processor won't even look at your XML document):
DECLARE #input XML = '<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<student_token>7</student_token>
<student_ssn>552</student_ssn>
<alternate_id>20</alternate_id>
<old_ssn xsi:nil="true" />
.........
</row>'
;WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema-instance' as xsi)
SELECT
FieldName = T.C.value('local-name(.)', 'varchar(50)'),
FieldValue = T.C.value('(.)[1]', 'varchar(500)'),
IsNIL = ISNULL(T.C.value('(#xsi:nil)[1]', 'bit'), 0)
FROM
#Input.nodes('/row/*') AS T(C)
This gives me an output something like:
FieldName FieldValue IsNIL
student_token 7 0
student_ssn 552 0
alternate_id 20 0
old_ssn 1
.....
Of course, all output is going to be of type varchar(500) now in the FieldValue column....
Updated my answer, based on Mikael Eriksson's answer, to include the IsNIL handling, too. Thanks Mikael for the inspiration! You deserve the nod and the accept vote!
You can convert your xml file to json and then use OPENJSON instead. Take a look at openjson : SELECT star on how to use OPENJSON without having to list column names.
To convert an xml file to json you can use sp_execute_external_script and the xmltodict python module.
Take a look at Importing Python Libraries to SQL Server to import the python module and deal with YAPI (Yet Another Python Install) issues.

sql select data from XML param

whats the SQL for selecting the values from this XML chunk like done in the sample below?
<RWFCriteria reportType="OPRAProject">
<item id="88" name="" value="" type="Project" />
<item id="112" name="" value="12" type="Milestone" />
<item id="43" name="" value="11" type="Milestone" />
</RWFCriteria>
i want to select out similar to this but with the above XML data
DECLARE #Param XML
SET #Param = '<data>
<release id="1"><milestone id="1" /><milestone id="2" /></release>
<release id="3"><milestone id="1" /><milestone id="27"/></release>
</data>'
SELECT c.value('../#id', 'INT') AS ReleaseId, c.value('#id', 'INT') AS MilestoneId
FROM #Param.nodes('/data/release/milestone') AS T(c)
I want only the data in the nodes where type="Milestone"
Something like this:
DECLARE #Param XML
SET #Param = '<RWFCriteria reportType="OPRAProject">
<item id="88" name="" value="" type="Project" />
<item id="112" name="" value="12" type="Milestone" />
<item id="43" name="" value="11" type="Milestone" />
</RWFCriteria>'
SELECT
RWF.item.value('#id', 'INT') AS 'Id',
RWF.item.value('#name', 'VARCHAR(100)') AS 'Name',
RWF.item.value('#value', 'INT') AS 'Value',
RWF.item.value('#type', 'VARCHAR(100)') AS 'Type'
FROM
#Param.nodes('/RWFCriteria/item') AS RWF(item)
WHERE
RWF.item.value('#type', 'VARCHAR(100)') = 'Milestone'
Resulting output:
Id Name Value Type
112 12 Milestone
43 11 Milestone

T-SQL Dynamic xquery

I am trying to figure out how I can load my table variable with data from XML using dynamic xquery? I am getting a result set of nodes from the query and defining the value type of those nodes. It seems that it is the value definition of the nodes that it is blowing up on.
Here is an example of the script that works, but is not dynamic.
Script:
DECLARE #XML XML = '<root>
<data>
<list id="organization" label="Organization">
<options>
<item value="1" label="Organization1" selected="false" />
<item value="2" label="Organization2" selected="false" />
<item value="3" label="Organization3" selected="false" />
<item value="4" label="Organization4" selected="true" />
<item value="5" label="Organization5" selected="true" />
</options>
</list>
</data>
</root>';
DECLARE #Orgs TABLE (ID INT);
Insert Into #Orgs(ID) Select OrgNameIdNodes.ID.value('#value','int') from #xml.nodes('//*[#id="organization"]//item[#selected="true"]') as OrgNameIdNodes(ID);
Select *
from #orgs
What I would like to be able to do is pass in parameters for both value and the #xml.nodes sections so I would have something like:
Insert Into #Orgs(ID) Select OrgNameIdNodes.ID.value(#Value) from #xml.nodes(#Nodes) as OrgNameIdNodes(ID);
Is this possible?
How about using sp_executesql with dynamic sql. Something like:
DECLARE #XML XML = '<root>
<data>
<list id="organization" label="Organization">
<options>
<item value="1" label="Organization1" selected="false" />
<item value="2" label="Organization2" selected="false" />
<item value="3" label="Organization3" selected="false" />
<item value="4" label="Organization4" selected="true" />
<item value="5" label="Organization5" selected="true" />
</options>
</list>
</data>
</root>';
declare #orgs table(ID int);
declare #nodes nvarchar(4000),
#value nvarchar(4000),
#query nvarchar(4000)
select #value = '''#value'',''int'''
select #nodes = '//*[#id="organization"]//item[#selected="true"]'
select #query = 'Select OrgNameIdNodes.ID.value( ' + #value + ') ' +
'from #xml.nodes(''' + #nodes + ''') as OrgNameIdNodes(ID)'
insert into #Orgs(ID) EXEC sp_executesql #query, N'#xml xml', #xml = #xml
Select *
from #orgs

How to replace attribute value of *all* matching elements with XQuery?

I'm trying without luck to create a modify() statement to change the value of an attribute in all elements that have that attribute value -- so far I can only get it to change the value in the first matched element. I created an example below of what I have so far, which I'm running in SQL Server 2005:
DECLARE #x XML
SELECT #x = '
<FootballApparel>
<Item Team="Phoenix Cardinals" Type="Hat" Cost="$14.99" />
<Item Team="Indianapolis Colts" Type="Hat" Cost="$14.99" />
<Item Team="Cincinnati Bengals" Type="Hat" Cost="$14.99" />
<Item Team="Phoenix Cardinals" Type="Shirt" Cost="$21.99" />
<Item Team="Indianapolis Colts" Type="Shirt" Cost="$21.99" />
<Item Team="Cincinnati Bengals" Type="Shirt" Cost="$21.99" />
</FootballApparel>
';
SET #x.modify('
replace value of
(/FootballApparel/Item[#Team="Phoenix Cardinals"]/#Team)[1]
with "Arizona Cardinals"
');
SELECT #x;
Running this gives the results below -- only the first instance of Phoenix Cardinals has been changed.
<FootballApparel>
<Item Team="Arizona Cardinals" Type="Hat" Cost="$14.99" />
<Item Team="Indianapolis Colts" Type="Hat" Cost="$14.99" />
<Item Team="Cincinnati Bengals" Type="Hat" Cost="$14.99" />
<Item Team="Phoenix Cardinals" Type="Shirt" Cost="$21.99" />
<Item Team="Indianapolis Colts" Type="Shirt" Cost="$21.99" />
<Item Team="Cincinnati Bengals" Type="Shirt" Cost="$21.99" />
</FootballApparel>
Can you please help me with the correct modify() statement to replace all instances?
Thanks!
Kevin
You're very close - what you need to do is loop (and there's no other way I know of to do it in this case) and repeatedly replace the values:
WHILE #x.exist('(/FootballApparel/Item[#Team=sql:variable("#oldTeamName")])[1]') = 1
SET #x.modify('
replace value of (
/FootballApparel/Item[#Team=sql:variable("#oldTeamName")]/#Team
)[1]
with sql:variable("#newTeamName")
');
That should do the trick.
Marc