XML Schema Collection breaks xquery - sql

The script below can be used to show an example of the problem I have. The script will return an error "There is no element named 'this'".
If I change the declaration of the #XML variable so it is untyped XML (replace the line DECLARE #XML XML(Test) with DECLARE #XML XML), I do not get the error, but get the expected result instead.
Why do I get the error when using the Schema collection and how can I avoid it - I want to fill a table column with properly typed xml, but I'm having trouble getting my data back out of the column.
IF EXISTS (SELECT name FROM sys.xml_schema_collections WHERE name = 'test') DROP XML SCHEMA COLLECTION Test
CREATE XML SCHEMA COLLECTION Test AS
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:t="http://tempuri.org/this.xsd"
targetNamespace="http://tempuri.org/this.xsd"
elementFormDefault="qualified">
<xsd:element name ="this">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="What" minOccurs="1" maxOccurs="1" >
<xsd:simpleType>
<xsd:restriction base ="xsd:string">
<xsd:enumeration value ="01."/>
<xsd:enumeration value ="02."/>
<xsd:enumeration value ="03."/>
<xsd:enumeration value ="04."/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="When" minOccurs="1" maxOccurs="1" >
<xsd:simpleType>
<xsd:restriction base ="xsd:string">
<xsd:enumeration value ="01."/>
<xsd:enumeration value ="02."/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Outcome" minOccurs="1" maxOccurs="1" >
<xsd:simpleType>
<xsd:restriction base ="xsd:string">
<xsd:enumeration value ="one"/>
<xsd:enumeration value ="two"/>
<xsd:enumeration value ="whatever"/>
<xsd:enumeration value =""/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>'
GO
DECLARE #XML XML(Test)
SET #XML =
'<this xmlns="http://tempuri.org/this.xsd">
<What>02.</What>
<When>01.</When>
<Outcome></Outcome>
</this>'
SELECT #XML
SELECT
Field.value('What[1]','VARCHAR(100)')
FROM #Xml.nodes('/this') AS this(Field)

The problem is your namespace declaration: <this xmlns="http://tempuri.org/this.xsd">. Everything in that XML snippet belongs to the namespace http://tempuri.org/this.xsd. Your query used the unqualified name for this and What. Use this instead:
;WITH XMLNAMESPACES (
'http://tempuri.org/this.xsd' as t
)
SELECT
Field.value('t:What[1]','VARCHAR(100)')
FROM #Xml.nodes('/t:this') AS this(Field)

Related

SQL string value

Have a large XML file stored in a field within a table, many values have already been extracted and stored in the table, but I'm looking to capture (2) additional: account type = "current" status="X" and account type = "former" status ="Y". In the partial output below there is no former account type so I need a strategy for missing as well.
<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://cp.com/rules/client">
<admin>
<product_reference>12345678901234</product_reference>
<report_type>XXXXXXX</report_type>
<status>XXXXXXXX</status>
<ownership>XXXXXXX</ownership>
<report_code>1234</report_code>
<report_description>XXXXXXXXXXXXXXXXX</report_description>
<purpose>XXXXXXXX</purpose>
<date_request_ordered>mm/dd/yyyy</date_request_ordered>
<date_request_received>mm/dd/yyyy</date_request_received>
<date_request_completed>mm/dd/yyyy</date_request_completed>
<time_report_processed>01234</time_report_processed>
<multiple_scores_ordered>false</multiple_scores_ordered>
<vendor name="XXXXXXXXXXXXX" address="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<report>
<sequence>0000000000</sequence>
<count>0000000000</count>
</report>
</admin>
<report>
<alerts_scoring>
<scoring>
<score status="XXXXXXXXXX">
<model_label>XXXXXXXXXXXXXXXXX</model_label>
<score>123</score>
<rating_state>XX</rating_state>
<classification>XXXXXXXXXXXXXXXXX</classification>
<reason_codes>
<code>05</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>04</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>10</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>27</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
</score>
</scoring>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
</alerts_scoring>
<vendor_dataset>
<subjects>
<subject type="Primary" relationship_to_data="Subject">
<name type="Report Subject">
<first>XXXX</first>
<middle>X</middle>
<last>XXXX</last>
</name>
<name type="Alias">
<first>XXXXXXXXXX</first>
<last>XXXXXXXX</last>
</name>
<birth_date>mm/dd/yyyy</birth_date>
<ssn>999999999</ssn>
<address type="residence" ref="1" />
<address type="former" ref="2" />
<address type="former" ref="3" />
</subject>
</subjects>
<addresses>
<address id="1">
<house>1234</house>
<street1>sample</street1>
<city>sample</city>
<state>XX</state>
<postalcode>12345</postalcode>
<zip4>1234</zip4>
<date_first_at_address>mm/dd/yyyy</date_first_at_address>
<date_last_at_address>mm/dd/yyyy</date_last_at_address>
</address>
<address id="X">
<house>1234</house>
<street1>XXXXXXXXX</street1>
<city>XXXXXXXXX</city>
<state>XX</state>
<postalcode>12345</postalcode>
<zip4>1234</zip4>
<date_first_at_address>mm/dd/yyyy</date_first_at_address>
<date_last_at_address>mm/dd/yyyy</date_last_at_address>
</address>
</addresses>
</vendor_dataset>
<summary>
<date_oldest_trade>mm/dd/yyyy</date_oldest_trade>
<date_latest_trade>mm/dd/yyyy</date_latest_trade>
<date_latest_activity>mm/dd/yyyy</date_latest_activity>
<includes_bankruptcies flag="false" />
<includes_other_records public_records="false" collection="false" consumer_statement="false" />
<credit_range high="12345" low="123" number_trade_lines="123" />
<account_status_counters>
<!-- here --> <account type="current" description="Pays Account as Agreed" status="1">12</account>
</account_status_counters>
<account_summaries>
<account type="Open-ended">
<number_accounts>0</number_accounts>
<total_owed>0</total_owed>
<total_past_due>0</total_past_due>
<high_amount>0</high_amount>
</account>
<account type="Revolving">
<number_accounts>00</number_accounts>
<total_owed>1234</total_owed>
<total_past_due>0</total_past_due>
<high_amount>12345</high_amount>
</account>
<account type="Installment">
<number_accounts>00</number_accounts>
<total_owed>12345</total_owed>
<total_past_due>0</total_past_due>
<high_amount>123456</high_amount>
</account>
</account_summaries>
<inquiry_history count="0" />
</summary>
<employment_history>
<employment_primary_subject>
<job entry="current" indirectly_verified="false">
<employer>
<name>XXXXXXXXXXX</name>
</employer>
</job>
<job entry="first_former" indirectly_verified="false">
<employer>
<name>XXXXXXXX</name>
<city>XXXXXXX</city>
<state>XX</state>
</employer>
</job>
</employment_primary_subject>
</employment_history>
<trade_account_activity>
<credit_trades>
<credit_trade automated_tape_supplier="false">
<reporting_member>
<number>1234X1234</number>
<name>XXX/1234</name>
</reporting_member>
<account>
<type>XXXXXXXXX</type>
<terms>XXX</terms>
<months_reviewed>00</months_reviewed>
<designator>XXXXXXXX(XXXXX)</designator>
</account>
<date_reported>mm/dd/yyyy</date_reported>
<date_opened>mm/dd/yyyy</date_opened>
<date_last_activity>mm/dd/yyyy</date_last_activity>
<current_rate>XXXXXXXXXXXXXXXXX</current_rate>
<highest_amount>1234</highest_amount>
<balance_amount>00</balance_amount>
<past_due_amount>00</past_due_amount>
<messages>
<message code="XX">XXXXXXXXXXXXXX</message>
<message code="XX">XXXXXXXXXXXXXX</message>
</messages>
</credit_trade>
</account>
<date_reported>mm/dd/yyyy</date_reported>
<date_opened>mm/dd/yyyy</date_opened>
<date_last_activity>mm/dd/yyyy</date_last_activity>
<current_rate>XXXXXXXXXXXXXXXXXXXXXX</current_rate>
<highest_amount>123456</highest_amount>
<balance_amount>123456</balance_amount>
<past_due_amount>0</past_due_amount>
<messages>
<message code="XX">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</message>
</messages>
</credit_trade>
</credit_trades>
</trade_account_activity>
<inquiry_history>
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345X1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345Y1234" />
<inquiry date="mm/dd/yyyy" name="CXXXXXXXXXXXXXXXXXXXXXXX" member="12345Z1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX & X" member="12345W1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345V1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345U1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345T1234" />
</inquiry_history>
</report>
</ncf_report>
I'm looking to extract the X value from account type = "current" status="X" and and Y value if an account type = "former" exists. In this case the value 1. added to XML to highlight area of interest. I started by pairing down the data set into a temp table.
select id,
LEFT(SUBSTRING(CreditscoreXML,charindex('<account type="current"',CreditscoreXML),charindex('</account>',CreditscoreXML)),charindex('">',SUBSTRING(CreditscoreXML,charindex('<account type="current"',CreditscoreXML),charindex('</account>',CreditscoreXML)))) [Current_Status]
select
Current_Status, --just so I see output is correct in temp table
substring(Current_Status, charindex('status="',
Current_Status)+8,len(Current_Status)-charindex('status',Current_Status)) [Current_Status]
from #TempCurrent
From here I further tried to refine the text search. Trying to figure out how to eliminate the " after 1 or a better solution to extract both current and former status, former can be missing, need this grouped by Id.
Current Output
Current_Worse_Score Current_Worse_Score Former_Worse
Original Text 1"
Rather than manipulating string data try using the built-in XML functions in SQL Server to make your life easier. For example:
create table dbo.Foo (
id int not null,
bar xml not null
);
insert dbo.Foo (id, bar) values (47, N'<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://cp.com/rules/client">
<!-- the rest of your xml... -->
</ncf_report>')
;with xmlnamespaces(default 'http://cp.com/rules/client')
select
id,
x.a.value(N'#description', N'nvarchar(50)') as [Description],
x.a.value(N'#status', N'nvarchar(50)') as [Status],
x.a.value(N'.', N'nvarchar(50)') as [Account]
from dbo.Foo
cross apply bar.nodes(N'/ncf_report/report/summary/account_status_counters/account[#type="current"]') x(a)
Which yields the result...
id Description Status Account
47 Pays Account as Agreed 1 12
You can use the built-in XML data type methods to query required values from an XML instance which is stored as XMLType column.
DECLARE #X XML;
SET #X = '<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" ....>'; -- provided XML instance
CREATE TABLE NCFREPORT
(
ncfreportcol XML NOT NULL
);
INSERT INTO ncfreport (ncfreportcol) values (#X); -- inserting the XML instance stored temporarily in above variable X
WITH xmlnamespaces ('http://cp.com/rules/client' as NR)
SELECT T.acc.value('(#status)[1]', 'int') AS Status,
T.acc.value('(#type)[1]', 'varchar(20)') AS AccType,
T.acc.value('(text())[1]', 'int') AS Acc
FROM ncfreport cross apply ncfreport.ncfreportcol.nodes ('/NR:ncf_report/NR:report/NR:summary/NR:account_status_counters/NR:account') as t(acc);
This will result in the following output:
Status AccType Acc
1 current 12
It will produce one row in the output for each account if you have multiple account tags defined in the XML instance. I also noticed that there are missing opening or closing tags in the above XML fragment. It would be a good idea to also have a look at validating the XML before entering into the table. Please have a look at various XML data type methods here - https://learn.microsoft.com/en-us/sql/t-sql/xml/xml-data-type-methods?view=sql-server-ver15

Convert XML column to Table Data in SQL Query

I have this XML file which is a returned data table from web service:
DECLARE #MyXML XML =
'<?xml version="1.0" encoding="utf-8"?>
<DataTable xmlns="http://tempuri.org/">
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Blah" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Blah">
<xs:complexType>
<xs:sequence>
<xs:element name="Col1" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<DocumentElement xmlns="">
<Blah diffgr:id="Blah1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Col1>testing1</Col1>
</Blah>
</DocumentElement>
</diffgr:diffgram>
</DataTable>'
I want an output like this:
Select Col1
Col1
testing1
Then, I would be able to insert the data into a table in sql.
Is this XML under your control?
I'm especially irritated by this
<DocumentElement xmlns="">
This line is redefining the default namespace.
The XML you show consists of a schema portion and a data portion
DECLARE #MyXML XML =
'<?xml version="1.0" encoding="utf-8"?>
<DataTable xmlns="http://tempuri.org/">
<!-- This schema is describing your data''s structure -->
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Blah" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Blah">
<xs:complexType>
<xs:sequence>
<xs:element name="Col1" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<!-- End of schema -->
<!-- Begin of data -->
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<DocumentElement xmlns="">
<Blah diffgr:id="Blah1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Col1>testing1</Col1>
</Blah>
</DocumentElement>
</diffgr:diffgram>
<!-- End of data -->
</DataTable>';
Your statement
Then, I would be able to insert the data into a table in sql.
... let's me think, that the real-world use case will consist of more than one column. In this case you do not show enough.
This query would read just the <Col1> content in a lazy approach:
SELECT #MyXML.value('(//*:Col1/text())[1]','nvarchar(max)');
In general it is good to be as specific as possible, but the actual issue looks, as if you might be better off with a namespace wildcard and a deep search (//) down to <DocumentElement>:
SELECT de.query('.')
FROM #MyXml.nodes('//DocumentElement') A(de)
This query will return various content of this area:
SELECT de.value('(Blah/#*:id)[1]','nvarchar(max)') DiffGr_ID
,de.value('(Blah/#*:rowOrder)[1]','int') MsData_RowOrder
,de.value('(Blah/#*:hasChanges)[1]','nvarchar(max)') diffgr_HasChanges
,de.value('(Blah/Col1/text())[1]','nvarchar(max)') Col1_Text
FROM #MyXml.nodes('//DocumentElement') A(de);
The result
DiffGr_ID MsData_RowOrder diffgr_HasChanges Col1_Text
-----------------------------------------------------------
Blah1 0 inserted testing1
INSERT INTO sampletable
(
col1,
col2
)
SELECT
t.value('(col1/text())[1]', 'nvarchar(10)') ---colum name of the xml
t.value('(col2/text())[1]', 'nvarchar(10)') ---colum name of the xml
FROM #xmlData.nodes('/NewDataSet/MYTABLE') AS TempTable(t)
take not ,when the type of the column in the xml is float you need to change the 'narchar(10)' to 'float', or if date use 'date', etc..
You need to set the XML Namespace before using the XQuery:
;WITH XMLNAMESPACES('http://tempuri.org/' AS ns, 'urn:schemas-microsoft-com:xml-diffgram-v1' AS dg)
SELECT
Col1 = XC.value('(Col1)[1]', 'varchar(50)')
FROM
#MyXML.nodes('/ns:DataTable/dg:diffgram/DocumentElement/Blah') AS XT(XC)

Stripping data from xml in SQL Server

One of my tables with xml datatype has the following xml information:
<RequestMetaData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MetaData Type="DocImport">
<Keywords>
<Key Name="Zone" Value="MIO" />
<Key Name="ClassificationStrategy" Value="NeedClassification" />
<Key Name="Folder" Value="0456e6ca" />
</Keywords>
</MetaData>
<MetaData Type="SourceResponse">
<Keywords>
<Key Name="NotificationResponse_20180427-150426" Value="Received successful response from Source" />
</Keywords>
</MetaData>
</RequestMetaData>
I need to write an SQL query to fetch the value of Classification strategy based on key name.
I have added the xml in a variable #xml and used the following code. It is returning NULL.
select A.b.value('ClassificationStrategy[1]', 'VARCHAR(30)') AS CS
FROM #xml.nodes('/RequestMetaData/MetaData/Keywords') AS A(b)
Can someone please help me with this.
You can read your XML in various ways. Use a simple .value() with an XPath/XQuery expression to retrieve a single value, use .query to retrieve a part of the XML or use .nodes() to return repeated elements as derived table:
DECLARE #xml XML=
N'<RequestMetaData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MetaData Type="DocImport">
<Keywords>
<Key Name="Zone" Value="MIO" />
<Key Name="ClassificationStrategy" Value="NeedClassification" />
<Key Name="Folder" Value="0456e6ca" />
</Keywords>
</MetaData>
<MetaData Type="SourceResponse">
<Keywords>
<Key Name="NotificationResponse_20180427-150426" Value="Received successful response from Source" />
</Keywords>
</MetaData>
</RequestMetaData>';
--Read the whole lot
SELECT md.value('#Type','nvarchar(max)') AS MetaDataType
,k.value('#Name','nvarchar(max)') AS KeyName
,k.value('#Value','nvarchar(max)') AS KeyValue
FROM #xml.nodes('/RequestMetaData/MetaData') A(md)
OUTER APPLY md.nodes('Keywords/Key') B(k);
--Get one key's value by name (anywhere in the doc)
DECLARE #keyName VARCHAR(100)='ClassificationStrategy';
SELECT #xml.value('(//Key[#Name=sql:variable("#keyName")]/#Value)[1]','nvarchar(max)');
--Use the meta data type as additional filter (if key names are not unique per doc)
DECLARE #kName VARCHAR(100)='ClassificationStrategy';
DECLARE #mdType VARCHAR(100)='DocImport';
SELECT #xml.value('(/RequestMetaData
/MetaData[#Type=sql:variable("#mdType")]
/Keywords
/Key[#Name=sql:variable("#kName")]
/#Value)[1]','nvarchar(max)');

How to read formated xml file in SQL Server

I have XML file that need to read some data from it by SQL Server 2008.
Please guide me to to solve this problem.
My XML file like that:
<?xml version="1.0" encoding="utf-8"?>
<DataSet xmlns="http://tempuri.org/">
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Table">
<xs:complexType>
<xs:sequence>
<xs:element name="CI_CODE" type="xs:decimal" minOccurs="0" />
<xs:element name="CI_NAME" type="xs:string" minOccurs="0" />
<xs:element name="CI_PISH_CODE" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<NewDataSet xmlns="">
<Table diffgr:id="Table1" msdata:rowOrder="0">
<CI_CODE>1</CI_CODE>
<CI_NAME>Kerman</CI_NAME>
<CI_PISH_CODE>34</CI_PISH_CODE>
</Table>
<Table diffgr:id="Table2" msdata:rowOrder="1">
<CI_CODE>2</CI_CODE>
<CI_NAME>Anar</CI_NAME>
<CI_PISH_CODE>34</CI_PISH_CODE>
</Table>
<Table diffgr:id="Table3" msdata:rowOrder="2">
<CI_CODE>3</CI_CODE>
<CI_NAME>Baft</CI_NAME>
<CI_PISH_CODE>34</CI_PISH_CODE>
</Table>
</NewDataSet>
</diffgr:diffgram>
</DataSet>
I need to help to read this XML in SQL Server 2008.
I need tags <CI_CODE> and <CI_NAME> and <CI_PISH_CODE> data.
Please guide me
Assuming your XML is stored in a variable called #Data XML, you can use this XQuery go get your data.
The XPath expression "navigates" down the tree of nodes to give you a "virtual" table of XML fragments - one for each <Table> XML element. From that XML element, you can then fetch the individual data items using the .value() XQuery expression.
You need to be especially vigilant about using the correct XML namespaces for each element.
;WITH XMLNAMESPACES('http://tempuri.org/' AS ns,
'urn:schemas-microsoft-com:xml-diffgram-v1' AS dg)
SELECT
CI_CODE = XC.value('(CI_CODE)[1]', 'int'),
CI_NAME = XC.value('(CI_NAME)[1]', 'varchar(50)'),
CI_PISH_CODE = XC.value('(CI_PISH_CODE)[1]', 'int')
FROM
#Data.nodes('/ns:DataSet/dg:diffgram/NewDataSet/Table') AS XT(XC)
This produces this output on my PC:
In general I'd advise you to be as specific as possible. In this case you have unclean namespaces (empty default namespace at schema level), which makes it difficult to query this correctly.
Try it wild wildcarded namespace (*:):
EDIT: simplified
--Your XML in a variable
DECLARE #xml XML=
'Copy your XML here';
--The query
SELECT dgt.value('(#*:id)[1]','nvarchar(max)') AS DiffgramTable
,dgt.value('(*:CI_CODE)[1]','int') AS Diffgram_CI_CODE
,dgt.value('(*:CI_NAME)[1]','nvarchar(max)') AS Diffgram_CI_NAME
,dgt.value('(*:CI_PISH_CODE)[1]','int') AS Diffgram_CI_CODE
FROM #xml.nodes('/*:DataSet') AS A(ds)
OUTER APPLY ds.nodes('*:diffgram') AS C(dg)
OUTER APPLY dg.nodes('*:NewDataSet/*:Table') AS D(dgt)
First we pick the "DataSet" and call it "ds". Below this we pick "diffgram" ("dg"). Below "dg" we pick all "Table" ("dgt")
The select will read all information out of the nearest node
The result
+---------------+------------------+------------------+------------------+
| DiffgramTable | Diffgram_CI_CODE | Diffgram_CI_NAME | Diffgram_CI_CODE |
+---------------+------------------+------------------+------------------+
| Table1 | 1 | Kerman | 34 |
+---------------+------------------+------------------+------------------+
| Table2 | 2 | Anar | 34 |
+---------------+------------------+------------------+------------------+
| Table3 | 3 | Baft | 34 |
+---------------+------------------+------------------+------------------+

SQL Server xquery sum cast error when schema data type is string

Trying to run this in SQL Server 2014 in order to sum all Values in "UserData" xml:
IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
DROP XML SCHEMA COLLECTION SC
go
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
go
Declare #xml xml(SC)
set #xml= '<UserData>
<Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
<Value>2</Value>
</Item>
<Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
<Value>4</Value>
</Item>
</UserData>'
Select #xml.value('sum(/UserData/Item[#Type="CONVERTED_PAGES"]/Value)','int') as Sum
and getting the following error:
Msg 9308, Level 16, State 1, Line 16
XQuery [value()]: The argument of 'sum()' must be of a single numeric primitive type or 'http://www.w3.org/2004/07/xpath-datatypes#untypedAtomic'. Found argument of type 'xs:string *'.
I tried changing the select to the following:
Select #xml.value('sum(/UserData/Item[#Type="CONVERTED_PAGES"]/Value cast as xs:int?)','int') as Sum
But then I get this:
Msg 2365, Level 16, State 1, Line 16 XQuery [value()]: Cannot
explicitly convert from 'xs:string *' to 'xs:int ?'
I am not able to change the xml schema in this case, but figured I could cast in order to perform this operation (since I know that in my case all of the Values will be int). Any suggestions would be appreciated!
The xquery sum aggregate requires the input to be a number. Currently it is defined as string in your XSD. To get this to work, you have three options:
Option 1:
You change the schema to force "value" to be an int. Instead of the first line below, use the second. (The difference is highlighted in between the two statements with "|||||||".)
Query 1:
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
|||||||
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:integer" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
Option 2:
If changing the XSD is not an option, you can also use the T-SQL SUM aggregate instead of the xquery one, like this:
Query 2:
IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
DROP XML SCHEMA COLLECTION SC
go
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
go
Declare #xml xml(SC)
set #xml= '<UserData>
<Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
<Value>2</Value>
</Item>
<Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
<Value>4</Value>
</Item>
</UserData>'
SELECT SUM(N.value('.','INT')) AS [Sum]
FROM #xml.nodes('/UserData/Item[#Type="CONVERTED_PAGES"]/Value') AS X(N);
Option 3:
As you noticed, SQL Server does not allow us to convert an XSD-typed value to another data type. To get around that, you could instruct SQL Server to forget about the schema:
Query 3:
IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
DROP XML SCHEMA COLLECTION SC;
GO
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>';
GO
DECLARE #xml XML(SC);
SET #xml= '<UserData>
<Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
<Value>2</Value>
</Item>
<Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
<Value>4</Value>
</Item>
</UserData>';
SELECT CAST(#xml AS XML).value('sum((/UserData/Item[#Type="CONVERTED_PAGES"]/Value ))','int') AS Sum;
Note: Without the schema, you still cannot cast (not sure why), but the sum now works without casting.
Update:
I did a little more digging. The original error message you got after attempting to cast is this one:
Msg 2365, Level 16, State 1, Line 16 XQuery [value()]: Cannot
explicitly convert from 'xs:string *' to 'xs:int ?'
It tells us that you can't convert a sequence of strings into a single integer.
The * as well as the ? are Occurrence Indicators. So the error message reads: zero-to-many strings can't be converted to zero-to-one integer.
Your xquery /UserData/Item[#Type="CONVERTED_PAGES"]/Value returns more than one value, and to sum them up we need to convert each one individually.
xquery offers multiple ways to accomplish that, but not all of them work in SQL Server. The one that works uses a for-each construct:
.value('sum(for $val in /UserData/Item[#Type="CONVERTED_PAGES"]/Value return $val cast as xs:int?)','INT');
Thanks to #MikaelEriksson for helping me out with this.