SQL Server XQuery returns an error - sql

I am performing a query against an XML data type column in SQL Server 2012. An example of the data is:
<ns:Resume xmlns:ns="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume">
<ns:Name>
<ns:Name.Prefix></ns:Name.Prefix>
<ns:Name.First>Shai</ns:Name.First>
<ns:Name.Middle></ns:Name.Middle>
<ns:Name.Last>Bassli</ns:Name.Last>
<ns:Name.Suffix></ns:Name.Suffix>
</ns:Name>
...
</ns:Resume>
I am trying to write a query to return the first names.
This query returns a list of first names as expected:
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume' AS ns)
SELECT [Resume].query('(//ns:Name.First)').value('.[1]', 'nvarchar(100)')
FROM HumanResources.JobCandidate;
However, this query returns an error:
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume' AS ns)
SELECT [Resume].value('(//ns:Name.First)[1]', 'nvarchar(100)')
FROM HumanResources.JobCandidate;
Error:
Msg 9314, Level 16, State 1, Line 2
XQuery [HumanResources.JobCandidate.Resume.value()]: Cannot implicitly atomize or apply 'fn:data()' to complex content elements, found type 'xs:anyType' within inferred type '(element(ns{http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume}:Name.First,xs:string) | element(ns{http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume}:Name.First,xs:anyType)) ?'.
There's some basic understanding that I'm missing here but I'm not sure what it is. Can someone enlighten me? Why does the second query return an error?

Your XPath expression could lead to multiple rows being returned, for each row in the SQL Server table. You'll need to use a CROSS APPLY and a call to .nodes() to get that information you're after:
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume' AS ns)
SELECT
JobCandidateID,
ResNames.value('(ns:Name.First)[1]', 'nvarchar(100)')
FROM
HumanResources.JobCandidate
CROSS APPLY
[Resume].nodes('/ns:Resume/ns:Name') AS XTbl(ResNames)
That should return all JobCandidateID values and all first names defined in the Resume XML column for each row in the table.
If you can be sure that there's only ever going to be a single <name> tag in your XML column, then you could also shorten this to:
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume' AS ns)
SELECT
JobCandidateID,
[Resume].value('(/ns:Resume/ns:Name/ns:Name.First)[1]', 'nvarchar(100)')
FROM
HumanResources.JobCandidate

Related

Issue to generate dynamic xml with XMLElement

I have a query: (show next below):
SELECT XMLElement("PetitionMarreCSV",
XMLAttributes('http://INT010.GPA.Schemas.External.GPA_AllFile' AS "xmlns"),
XMLForest(EVENT as "Event",INSERTDATE as "InsertDate",USER_ID as "USER_ID",FIRSTNAME as "FirstName",NAME as "Name",EMAIL as "Email",LANGUAGE as "Language",COUNTRY as "COUNTRY",USER_PROFILE_PIC as "USER_PROFILE_PIC",USER_PROFILE_VIDEO as "USER_PROFILE_VIDEO",OPTIN as "OPTIN",USER_WISTIA_VIDEO as "USER_WISTIA_VIDEO",USER_ACEPT as "USER_ACEPT",USER_APROVED as "USER_APROVED",STRET as "STRET",CITY as "City",POSTALCODE as "PostalCode",TELEPHONE as "Telephone",USER_INTERESTS as "USER_INTERESTS",USER_VIDEO_VIEWS as "USER_VIDEO_VIEWS",USER_SORT_ORDER as "USER_SORT_ORDER",DALING_VAN_DE_KOPKRACHT as "DALING_VAN_DE_KOPKRACHT",TOEGANG_TOT_DE_GEZONDHEIDSZORG as "TOEGANG_TOT_DE_GEZONDHEIDSZORG",RISICO_OP_EN_BLACK_OUT as "RISICO_OP_EN_BLACK_OUT",GEPLANDE_VEROUDERING as "GEPLANDE_VEROUDERING",VERHOGING_VERZEKERINGSPRIJZEN as "VERHOGING_VERZEKERINGSPRIJZEN",DURE_INTERNET as "DURE_INTERNET",TREINVERTRAGINGEN as "TREINVERTRAGINGEN",TRAGHEID_VAN_JUSTITIE as "TRAGHEID_VAN_JUSTITIE",LAGE_RENDEMENT_SPAREKENING as "LAGE_RENDEMENT_SPAREKENING",ONGEZONDE_JUNKFOD as "ONGEZONDE_JUNKFOD",MINDER_POSTKANTOREN as "MINDER_POSTKANTOREN",OPLICHTERIJ_EN_BEDROG as "OPLICHTERIJ_EN_BEDROG",VERKOPERS_AN_HUIS as "VERKOPERS_AN_HUIS",FILENAME_SOURCE as "FileName_Source")) AS "RESULT"
FROM (SELECT EVENT,INSERTDATE,USER_ID,FIRSTNAME,NAME,EMAIL,LANGUAGE,COUNTRY,USER_PROFILE_PIC,USER_PROFILE_VIDEO,OPTIN,USER_WISTIA_VIDEO,USER_ACEPT,USER_APROVED,STRET,CITY,POSTALCODE,TELEPHONE,USER_INTERESTS,USER_VIDEO_VIEWS,USER_SORT_ORDER,DALING_VAN_DE_KOPKRACHT,TOEGANG_TOT_DE_GEZONDHEIDSZORG,RISICO_OP_EN_BLACK_OUT,GEPLANDE_VEROUDERING,VERHOGING_VERZEKERINGSPRIJZEN,DURE_INTERNET,TREINVERTRAGINGEN,TRAGHEID_VAN_JUSTITIE,LAGE_RENDEMENT_SPAREKENING,ONGEZONDE_JUNKFOD,MINDER_POSTKANTOREN,OPLICHTERIJ_EN_BEDROG,VERKOPERS_AN_HUIS,FILENAME_SOURCE
FROM ops$kli.GPA22_20150130
GROUP BY EVENT,INSERTDATE,USER_ID,FIRSTNAME,NAME,EMAIL,LANGUAGE,COUNTRY,USER_PROFILE_PIC,USER_PROFILE_VIDEO,OPTIN,USER_WISTIA_VIDEO,USER_ACEPT,USER_APROVED,STRET,CITY,POSTALCODE,TELEPHONE,USER_INTERESTS,USER_VIDEO_VIEWS,USER_SORT_ORDER,DALING_VAN_DE_KOPKRACHT,TOEGANG_TOT_DE_GEZONDHEIDSZORG,RISICO_OP_EN_BLACK_OUT,GEPLANDE_VEROUDERING,VERHOGING_VERZEKERINGSPRIJZEN,DURE_INTERNET,TREINVERTRAGINGEN,TRAGHEID_VAN_JUSTITIE,LAGE_RENDEMENT_SPAREKENING,ONGEZONDE_JUNKFOD,MINDER_POSTKANTOREN,OPLICHTERIJ_EN_BEDROG,VERKOPERS_AN_HUIS,FILENAME_SOURCE)
This query create a list of CLOB value in xml format. But, by some reason that I don't know, sometimes retrieve the next error for some CLOB result instead to show the xml value:
Error: XML document must have a top level element.
The strange thing is, if I retrieve (filter the query) only with the row who has the error instead of multiples rows, the value with the error is not anymore.
SELECT XMLElement("PetitionMarreCSV",
XMLAttributes('http://INT010.GPA.Schemas.External.GPA_AllFile' AS "xmlns"),
XMLForest(EVENT as "Event",INSERTDATE as "InsertDate",USER_ID as "USER_ID",FIRSTNAME as "FirstName",NAME as "Name",EMAIL as "Email",LANGUAGE as "Language",COUNTRY as "COUNTRY",USER_PROFILE_PIC as "USER_PROFILE_PIC",USER_PROFILE_VIDEO as "USER_PROFILE_VIDEO",OPTIN as "OPTIN",USER_WISTIA_VIDEO as "USER_WISTIA_VIDEO",USER_ACEPT as "USER_ACEPT",USER_APROVED as "USER_APROVED",STRET as "STRET",CITY as "City",POSTALCODE as "PostalCode",TELEPHONE as "Telephone",USER_INTERESTS as "USER_INTERESTS",USER_VIDEO_VIEWS as "USER_VIDEO_VIEWS",USER_SORT_ORDER as "USER_SORT_ORDER",DALING_VAN_DE_KOPKRACHT as "DALING_VAN_DE_KOPKRACHT",TOEGANG_TOT_DE_GEZONDHEIDSZORG as "TOEGANG_TOT_DE_GEZONDHEIDSZORG",RISICO_OP_EN_BLACK_OUT as "RISICO_OP_EN_BLACK_OUT",GEPLANDE_VEROUDERING as "GEPLANDE_VEROUDERING",VERHOGING_VERZEKERINGSPRIJZEN as "VERHOGING_VERZEKERINGSPRIJZEN",DURE_INTERNET as "DURE_INTERNET",TREINVERTRAGINGEN as "TREINVERTRAGINGEN",TRAGHEID_VAN_JUSTITIE as "TRAGHEID_VAN_JUSTITIE",LAGE_RENDEMENT_SPAREKENING as "LAGE_RENDEMENT_SPAREKENING",ONGEZONDE_JUNKFOD as "ONGEZONDE_JUNKFOD",MINDER_POSTKANTOREN as "MINDER_POSTKANTOREN",OPLICHTERIJ_EN_BEDROG as "OPLICHTERIJ_EN_BEDROG",VERKOPERS_AN_HUIS as "VERKOPERS_AN_HUIS",FILENAME_SOURCE as "FileName_Source")) AS "RESULT"
FROM (SELECT EVENT,INSERTDATE,USER_ID,FIRSTNAME,NAME,EMAIL,LANGUAGE,COUNTRY,USER_PROFILE_PIC,USER_PROFILE_VIDEO,OPTIN,USER_WISTIA_VIDEO,USER_ACEPT,USER_APROVED,STRET,CITY,POSTALCODE,TELEPHONE,USER_INTERESTS,USER_VIDEO_VIEWS,USER_SORT_ORDER,DALING_VAN_DE_KOPKRACHT,TOEGANG_TOT_DE_GEZONDHEIDSZORG,RISICO_OP_EN_BLACK_OUT,GEPLANDE_VEROUDERING,VERHOGING_VERZEKERINGSPRIJZEN,DURE_INTERNET,TREINVERTRAGINGEN,TRAGHEID_VAN_JUSTITIE,LAGE_RENDEMENT_SPAREKENING,ONGEZONDE_JUNKFOD,MINDER_POSTKANTOREN,OPLICHTERIJ_EN_BEDROG,VERKOPERS_AN_HUIS,FILENAME_SOURCE
FROM ops$kli.GPA22_20150130
WHERE user_id = 293
GROUP BY EVENT,INSERTDATE,USER_ID,FIRSTNAME,NAME,EMAIL,LANGUAGE,COUNTRY,USER_PROFILE_PIC,USER_PROFILE_VIDEO,OPTIN,USER_WISTIA_VIDEO,USER_ACEPT,USER_APROVED,STRET,CITY,POSTALCODE,TELEPHONE,USER_INTERESTS,USER_VIDEO_VIEWS,USER_SORT_ORDER,DALING_VAN_DE_KOPKRACHT,TOEGANG_TOT_DE_GEZONDHEIDSZORG,RISICO_OP_EN_BLACK_OUT,GEPLANDE_VEROUDERING,VERHOGING_VERZEKERINGSPRIJZEN,DURE_INTERNET,TREINVERTRAGINGEN,TRAGHEID_VAN_JUSTITIE,LAGE_RENDEMENT_SPAREKENING,ONGEZONDE_JUNKFOD,MINDER_POSTKANTOREN,OPLICHTERIJ_EN_BEDROG,VERKOPERS_AN_HUIS,FILENAME_SOURCE)
Do you know if there is a problem with this function when you try to generate several CLOB columns?

xml query with sql

I have an XML column:
<xmlList>
<XMLEntity>
<sug>ACHER</sug>
</XMLEntity>
<XMLEntity>
<sug>DOA</sug>
</XMLEntity>
</xmlList>
In this way I get just the sug node:
SELECT XMLSERIALIZE(XMLQUERY ('//xmlList/XMLEntity/sug' passing
KTOVET ) as char large object) as XXX
FROM "TABLE"
How can I get the sug nodes where its value is "ACHER"?
You can try using this XQuery expression :
//xmlList/XMLEntity/sug[.="ACHER"]

Updating an sql xml field which has xmlnamespaces

I have an xml string which I can read the value of the ID out with
;WITH XMLNAMESPACES
('http://collaborate.com/svn/capabilities/tdp/ManageNetworkAndServiceDiagnosticsV4/' AS P,
'https://collaborate.com/svn/edm/tdp/Test' as p2)
Select #id = feed.xx.value('.','VARCHAR(50)')
From #smxml.nodes('/p:initiateTest/p2:Test/p2:id') feed(xx)
This sets the #ID variable, which I amend, and then I want to place it back into the table.
I have tried
;WITH XMLNAMESPACES
('http://collaborate.com/svn/capabilities/tdp/ManageNetworkAndServiceDiagnosticsV4/' AS p,
'https://collaborate.com/svn/edm/tdp/Test' as p2)
UPDATE CT_GTCS_Temp_XML
SET xmlStartString.modify('replace value of (/p:initiateTest/p2:Test/p2:id with #New_Id)')
BUt I get an error of
XQuery [CT_GTCS_Temp_XML.xmlStartString.modify()]: ")" was expected.
As the error message suggested, you didn't put the closing bracket in correct place. Put it right after the XQuery, not after with clause :
.....
SET xmlStartString.modify('replace value of
(/p:initiateTest/p2:Test/p2:id/text())[1]
with sql:variable("#New_Id")')

Using Fields[0].Value to get XML from FOR XML RAW, ELEMENTS query is messed up

I have a query that uses FOR XML RAW, ELEMENTS to return a SELECT query as a structured XML document. However, when I get the result using a TSQLDataSet by using Fields[0].Value, the result is different from what I see when I run the query in SQL Server Management Studio.
What I see in the result from the TSQLDataSet:
੄customerIdфname၄governmentNumberไdebtorAddress1ไdebtorAddress2ไdebtorAddress3ไdebtorAddress4ࡄpostCodeୄcontactNameՄphonë́faxൄcustomerSinceՄtermsلactiveไcurrentBalanceلDebtorခŁ䄁ഃӤ
What I see in the result in SSMS:
<Debtor>
<customerId>C0E449E5B2C </customerId>
<name>New Customer 2 </name>
<governmentNumber> </governmentNumber>
<debtorAddress1>Address Line 1 </debtorAddress1>
<debtorAddress4>Address Line 4 </debtorAddress4>
<postCode>1234 </postCode>
<phone>1234567890 </phone>
<fax>1234567890 </fax>
<customerSince>2013-06-10T18:16:06.213</customerSince>
<terms>M </terms>
<active>true</active>
<currentBalance>0.0000</currentBalance>
</Debtor>
Is there a particular way it should be executed to get the right result?
AFAIK this is a DbExpress limitation. I know how overcome this, but using ADO (the returned data must be requested using a special parametrized object and a set of ADO streams). However you can use a workaround converting the XML data to a string in the server side sorrounding the sentence with a select (subquery) or just using a simple CAST statement.
For example if you sentence is like so
SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS
you can rewrite to
SELECT (SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS)
or you can rewrite to (use a CAST VARCHAR or NVARCHAR)
SELECT CAST( (SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS) AS VARCHAR(MAX))
and finally
Retrieve the result like this
SQLDataSet1.Fields[0].AsString

Trying to get all xml nodes with SQL Server, what am I doing wrong?

I have the following xml in a table cell, in column MyColumn:
<BSDL xmlns="..." xmlns:i="...">
<dateTime>2012-12-30T00:00:00Z</dateTime>
<dateTime>2013-01-07T00:00:00Z</dateTime>
<dateTime>2013-01-14T00:00:00Z</dateTime>
<dateTime>2013-01-21T00:00:00Z</dateTime>
<dateTime>2013-01-29T00:00:00Z</dateTime>
<dateTime>2013-02-05T00:00:00Z</dateTime>
<dateTime>2013-02-12T00:00:00Z</dateTime>
<dateTime>2013-02-19T00:00:00Z</dateTime>
<dateTime>2013-03-22T00:00:00Z</dateTime>
<dateTime>2013-03-29T00:00:00Z</dateTime>
<dateTime>2013-04-19T00:00:00Z</dateTime>
</BSDL>
I'm just trying to query it (get all xml nodes) using:
SELECT BSDL.item.value('(dateTime)[1]', 'datetime')
from [MyTable]
CROSS APPLY [MyColumn].nodes ('//BSDL') BSDL(item)
it yields no result, although my MyColumn in MyTable has the same number of entries as above, for each row.
Since there's only one <BSDL> node - your call to //BSDL selects that single node and then item.value('(dateTime)[1]', 'datetime') returns the first <dateTime> child.
You need to change your XQuery to (use that xmlns=.... namespace you mention in your sample here):
;WITH XMLNAMESPACES('.....' as ns)
SELECT
item.value('.', 'datetime')
from
dbo.MyTable
CROSS APPLY
MyColumn.nodes ('/ns:BSDL/ns:dateTime') BSDL(item)
You need to get a list of all <dateTime> nodes under <BSDL> - then you'll get all of them and you can inspect them one by one.