SQL Using OpenXML to retrieve multiple Elements - sql-server

I'm querying using OpenXML to retrieve the cap elements between the subject elements in XML I don't want the cap between the support elemements. The query works great to retrieve one value but fails when there are multiple element nodes.
<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>
CREATE Table #XmlTemp(XmlField Xml);
Set Nocount On;
Insert Into #XmlTemp(XmlField)
Select '<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>'As XmlField;
Declare #xmlData Xml;
Select #xmlData = XmlField From #XmlTemp;
Declare #document int;
Exec sp_xml_preparedocument #document Output, #xmlData, NULL;
SELECT ID,Cap FROM(
SELECT ID,Cap FROM OpenXml(#document,'./First/Test', 0) With (ID varchar(max)'./#id', Cap Varchar(max) './subject/cap')) alias
drop table #xmltemp
It'd be fairly time consuming to change the query to use .nodes method more so because of the testing involved so I'd like it to stay as OpenXML if possible.
I'd only like to retrieve out the ID and then the multiple cap element values.
Thank you for your time.

I can't see why the query using .nodes is complex. Just
SELECT t.n.value('(/First/Test/#id)[1]', 'int') id
, t.n.value('(.)[1]', 'int') cap
from #xmlData.nodes('./First/Test/subject/cap') t(n);
And OpenXML version
SELECT ID,Cap FROM(
SELECT ID,Cap
FROM OpenXml(#document,'./First/Test/subject/cap', 0)
With (ID varchar(max) '/First/Test/#id'
, Cap Varchar(max) '.')) alias
Version for the edited question
SELECT ID,Cap FROM(
SELECT ID,Cap
FROM OpenXml(#document,'/First/Test/subject/cap', 0)
With (ID varchar(max) '../../#id'
, Cap Varchar(max) '.')) alias
It returns only subject/cap and #id of the proper parent:
ID Cap
1 83847 15
2 83847 25
3 83847 100
4 83848 150
5 83848 2
6 83848 10

Your XML is double nested. You have 1:n of <Test> elements within <First> and again 1:n of <cap> elements within <subject>.
The proper way to query this is diving into the XML strictly forward:
CREATE Table #XmlTemp(XmlField Xml);
Set Nocount On;
Insert Into #XmlTemp(XmlField)
Select '<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>'As XmlField;
--The query will use .nodes() to get all <Test> elements and again .nodes() to get the related <cap> elements:
SELECT t.value('#id', 'int') id
,c.value('text()[1]', 'int') cap
from #XmlTemp AS tbl
CROSS APPLY tbl.XmlField.nodes('/First/Test') AS A(t)
CROSS APPLY A.t.nodes('subject/cap') AS B(c);
GO
DROP TABLE #XmlTemp;

Related

Manipulate XMLNamespaces

I have the following output from SQL using FOR XML clause:
<q17:DestinationSection xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationCode>1</q17:DestinationCode>
<q17:DestinationName>Strada Rampei 9, Iasi</q17:DestinationName>
<q17:DestinationAddress1>Strada Rampei 9, Iasi</q17:DestinationAddress1>
<q17:DestinationAddress2>
xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
</q17:DestinationAddress2>
</q17:DestinationSection>
The DestinationSection is the main root for this block of data. Is there any possibility to do some workaround and to have something like below in the <q17:DestinationAddress2></q17:DestinationAddress2> tag?
<q17:DestinationAddress2 xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</q17:DestinationAddress2>
I have tried something but I get an error that says that I need to declare the namespace but I really don't know where to "introduce" that definition.
My SQL statement
DECLARE #XMLFINAL VARCHAR(MAX)
SET #XMLFINAL=''
DECLARE #XMLFINAL2 VARCHAR(MAX)
SET #XMLFINAL2=''
DECLARE #NUMBER NVARCHAR(100)
DECLARE #NUMBER2 NVARCHAR(100)
DECLARE #XML VARCHAR(MAX)
DECLARE #XML2 VARCHAR(MAX)
DECLARE Rec CURSOR FAST_FORWARD FOR
SELECT GID FROM PurchaseDocumentsHeader
OPEN Rec
FETCH NEXT FROM Rec INTO #NUMBER2
WHILE ##FETCH_STATUS = 0
BEGIN
SET #XML2=''
;WITH XMLNAMESPACES ('http://ITrack.Transmission/2011/02/25/Objects' as q17)
SELECT #XML2= (
SELECT
DestCode AS 'q17:DestinationCode', DestDescr 'q17:DestinationName', DestAddr AS 'q17:DestinationAddress1', DestAddr2 AS 'q17:DestinationAddress2',
DestZIP AS 'q17:DestinationZIP'
FROM PurchaseDocumentsHeader WHERE GID=#NUMBER2
FOR XML RAW('q17:DestinationSection'),ELEMENTS
)
FETCH NEXT FROM Rec INTO #NUMBER2
SET #XMLFINAL2=#XMLFINAL2+#XML2
END
CLOSE Rec DEALLOCATE Rec
EDIT
Please find below my DDL. It's a view used to extract the data from an official table.
CREATE VIEW [dbo].[PurchaseDocumentsHeader]
AS
SELECT esd.GID, esd.ADRegistrationDate, esgo.Code AS DestCode, esgo.Description AS DestDescr, esgo.Address1 AS DestAddr, esgo.fCityCode AS DestCity,
esgp.TaxRegistrationNumber AS DestZIP, 'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestAddr2, esgo.Description AS DestRomanized,
esgo.Address1 AS DestAddress1Romanized,
'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestAddress2Romanized, esgp.TaxRegistrationNumber AS DestZIPRom,
esgo.fCityCode AS DestCityRomanized, esgo.fCountryCode AS DestCountry, cast('DestinationGLN xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>' AS XML) AS DestGLN,
'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestCoord
FROM ESFIDocumentTrade esd
LEFT JOIN ESGOSites esgo on esd.fDeliverySiteGID=esgo.GID
LEFT JOIN ESFITradeAccount esc on esd.fTradeAccountGID=esc.GID
LEFT JOIN ESGOPerson esgp on esc.fPersonCodeGID=esgo.GID
LEFT JOIN ESFIDocumentType est on esd.fADDocumentTypeGID=est.GID
WHERE esd.fTransitionStepCode='APPROVED' AND est.Code='CVR'
AND YEAR(esd.ADRegistrationDate)=YEAR(GETDATE())
AND MONTH(esd.ADRegistrationDate)=MONTH(GETDATE())
AND DAY(esd.ADRegistrationDate)=DAY(GETDATE())
GO
Later edit
CREATE TABLE DocPurcharseHeader
(
ADRegistrationDate date,
Code NVARCHAR(4000),
Description NVARCHAR(4000),
Address1 NVARCHAR (4000),
Address2 NVARCHAR (4000),
City NVARCHAR (4000),
ZIPCode NVARCHAR(4000)
)
INSERT INTO DocPurcharseHeader (ADRegistrationDate, Code, Description, Address1, Address2, City, ZIPCode)
VALUES('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', 'MyPOSAddress2', 'BUCHAREST', '123456')
2nd Later edit
;WITH XMLNAMESPACES ('http://ITrack.Transmission/2011/02/25/Objects' as q17)
SELECT #XMLSalesOrders=(
SELECT DestCode AS [q17:DestinationCode]
,DestDescr AS [q17:DestinationName]
,DestAddr AS [q17:DestinationAddress1]
,DestAddr2 AS [q17:DestinationAddress2]
FROM PurchaseDocumentsHeader
FOR XML PATH('q17:DestinationSection'),ELEMENTS XSINIL,ROOT('q17:DestinationSections'))
This code above is generating the below output, without XSINL directive:
<q17:DestinationSections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationSection>
<q17:DestinationCode>1</q17:DestinationCode>
<q17:DestinationName>Strada Rampei 9, Iasi</q17:DestinationName>
<q17:DestinationAddress1>Strada Rampei 9, Iasi</q17:DestinationAddress1>
<q17:DestinationAddress2/>
</q17:DestinationSection>
<q17:DestinationSection>
you are trying to out-trick the very mighty XML engine.
As stated in your other question:
Never do this in a CURSOR! They are bad and evil, coming directly of the hell of procedural thinking and were invented by the devil of spaghetti code...
Try it like this:
I use your table DDL an insert some data. The second row will have a NULL in address2. Normally XML will simply omit NULL values. A not existing node is read as a NULL value. But you can force NULLs to be introduced as xsi.nil="true" with ELEMENTS XSINIL:
CREATE TABLE DocPurcharseHeader
(
ADRegistrationDate date,
Code NVARCHAR(4000),
Description NVARCHAR(4000),
Address1 NVARCHAR (4000),
Address2 NVARCHAR (4000),
City NVARCHAR (4000),
ZIPCode NVARCHAR(4000)
);
INSERT INTO DocPurcharseHeader (ADRegistrationDate, Code, Description, Address1, Address2, City, ZIPCode)
VALUES('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', 'MyPOSAddress2', 'BUCHAREST', '123456')
,('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', NULL, 'BUCHAREST', '123456');
WITH XMLNAMESPACES('http://ITrack.Transmission/2011/02/25/Objects' AS q17)
SELECT Code AS [q17:DestinationCode]
,Description AS [q17:DestinationName]
,Address1 AS [q17:DestinationAddress1]
,Address2 AS [q17:DestinationAddress2]
FROM DocPurcharseHeader
FOR XML PATH('q17:DestinationSection'),ELEMENTS XSINIL,ROOT('q17:DestinationSections');
The result
<q17:DestinationSections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationSection>
<q17:DestinationCode>01</q17:DestinationCode>
<q17:DestinationName>MyPOS</q17:DestinationName>
<q17:DestinationAddress1>MyPOSAddress1</q17:DestinationAddress1>
<q17:DestinationAddress2>MyPOSAddress2</q17:DestinationAddress2>
</q17:DestinationSection>
<q17:DestinationSection>
<q17:DestinationCode>01</q17:DestinationCode>
<q17:DestinationName>MyPOS</q17:DestinationName>
<q17:DestinationAddress1>MyPOSAddress1</q17:DestinationAddress1>
<q17:DestinationAddress2 xsi:nil="true" />
</q17:DestinationSection>
</q17:DestinationSections>
UPDATE: About NULL values:
Try this
DECLARE #DummyTable TABLE(SomeDescription VARCHAR(500), SomeValue VARCHAR(100));
INSERT INTO #DummyTable VALUES('A real NULL value',NULL)
,('An empty string','')
,('A blank string',' ')
,('Some Text','blah blah');
WITH XMLNAMESPACES('SomeURL' AS q17)
SELECT SomeDescription AS [q17:Description]
,SomeValue AS [q17:Value]
FROM #DummyTable
FOR XML PATH('q17:row'),ELEMENTS XSINIL,ROOT('root');
To get this
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="SomeURL">
<q17:row>
<q17:Description>A real NULL value</q17:Description>
<q17:Value xsi:nil="true" />
</q17:row>
<q17:row>
<q17:Description>An empty string</q17:Description>
<q17:Value></q17:Value>
</q17:row>
<q17:row>
<q17:Description>A blank string</q17:Description>
<q17:Value> </q17:Value>
</q17:row>
<q17:row>
<q17:Description>Some Text</q17:Description>
<q17:Value>blah blah</q17:Value>
</q17:row>
</root>
You can see, that the real NULL is encoded as xsi:nil="true", while the empty string is shown as <q17:Value></q17:Value> (which is exactly the same as <q17:Value />).
Check this answer for some examples about NULL and empty. Check this answer to understand more about text()

SQL Server FOR XML with nested sub-select

The following sql code
SELECT
b.GivenName [Individual/Name/FirstName] ,
b.FamilyName [Individual/Name/LastName] ,
b.Address_Country [Individual/Address/CountryCode] ,
b.AddressFree [Individual/Address/AddressFree]
FROM dbo.stage_Clients_Merge b
WHERE b.DWHClientNo = #dwhclientNo
FOR XML PATH('Owner'), TYPE
will generate an xml fragment
<Owner>
<Individual>
<Name>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Name>
<Address>
<CountryCode>US</CountryCode>
<AddressFree>123,Utah</AddressFree>
</Address>
</Individual>
</Owner>
If I extend this query to include a sub-query to return, for example, all the orders associated with a individual using the following:
(SELECT
c.OrderIdentificationNumber [Individual/OrderNumber]
FROM
dbo.stage_Customer_Orders c
WHERE
c.CustomerNo = b.masterClient
FOR
XML PATH(''), TYPE)
I will get the individual section twice. How can I combine the two to return the following?
<Owner>
<Individual>
<OrderNo>12345</OrderNo>
<OrderNo>23456</OrderNo>
<Name>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Name>
<Address>
<CountryCode>US</CountryCode>
<AddressFree>123,Utah</AddressFree>
</Address>
</Individual>
</Owner>
Try it like this
I set up a mock-up scenario for stand-alone solutions. Next time you have a question, you might try this on your own. This makes it much easier to understand your issue and create a fully working and testable solution for it.
DECLARE #stage_Clients_Merge TABLE(ID INT,FirstName VARCHAR(100),LastName VARCHAR(100),CountryCode VARCHAR(100),AdressFree VARCHAR(100));
INSERT INTO #stage_Clients_Merge VALUES
(1,'Bob','Smith','US','123/Utah')
,(2,'Jim','Doh','US','123/Maryland');
DECLARE #orders TABLE(CustomerID INT, OrderNo INT)
INSERT INTO #orders VALUES
(1,11111),(1,12222)
,(2,21111),(2,22222);
SELECT (SELECT OrderNo FROM #orders WHERE CustomerID=b.ID FOR XML PATH(''),TYPE) AS Individual
,FirstName AS [Individual/Name/FirstName]
,LastName AS [Individual/Name/LastName]
,CountryCode AS [Individual/Address/CountryCode]
,AdressFree AS [Individual/Address/AdressFree]
FROM #stage_Clients_Merge AS b
FOR XML PATH('Owner');
You can use a subquery and [data()] to just get the column values with no additional nesting - then use path as before:
SELECT
(SELECT c.OrderIdentificationNumber as [data()]
FROM dbo.stage_Customer_Orders c
WHERE c.CustomerNo = b.masterClient
For XML Path('OrderNo'), type) as [Individual],
b.GivenName [Individual/Name/FirstName] ,
b.FamilyName [Individual/Name/LastName] ,
b.Address_Country [Individual/Address/CountryCode] ,
b.AddressFree [Individual/Address/AddressFree]
FROM dbo.stage_Clients_Merge b
WHERE b.DWHClientNo = #dwhclientNo
FOR XML PATH('Owner'), TYPE

Retrieving an XML node value with TSQL?

What am I not getting here? I can't get any return except NULL...
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
DECLARE #nodeVal int
SELECT #nodeVal = #xml.value('(errorFlag)[1]', 'int')
SELECT #nodeVal
Here is the solution:
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
declare #table table (data xml);
insert into #table values (#xml);
WITH xmlnamespaces (
'http://schemas.xmlsoap.org/soap/envelope/' as [soap])
SELECT Data.value('(/soap:Envelope/soap:Body/webregdataResponse/errorFlag)[1]','int') AS ErrorFlag
FROM #Table ;
Running the above SQL will return 99.
Snapshot of the result is given below,
That's because errorFlag is not the root element of your XML document. You can either specify full path from root element to errorFlag, for example* :
SELECT #nodeVal = #xml.value('(/*/*/*/errorFlag)[1]', 'int')
or you can use descendant-or-self axis (//) to get element by name regardless of it's location in the XML document, for example :
SELECT #nodeVal = #xml.value('(//errorFlag)[1]', 'int')
*: I'm using * instead of actual element name just to simplify the expression. You can also use actual element names along with the namespaces, like demonstrated in the other answer.

Generate complex XML with T-SQL syntax

I am trying to build a query that would return more or less complex XML structure. This is expected output I would like to have:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IGeocodeService/HereRouteMatchExtension</a:Action>
<a:MessageID>urn:uuid: some_messageID</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">ServiceURL</a:To>
</s:Header>
<s:Body>
<HereRouteMatchExtension xmlns="http://tempuri.org/">
<vehicleTrace xmlns:b="http://schemas.datacontract.org/2004/07/FMCommonTypes.WCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<s:Latitude>2</s:Latitude>
<s:Longitude>2</s:Longitude>
<s:PositionGuid>577AF773-C7A8-4D65-82DA-37A15CC7611D</s:PositionGuid>
</vehicleTrace>
</HereRouteMatchExtension>
</s:Body>
</s:Envelope>
I am using following code:
CREATE TABLE #test
(
Latitude INT,
Longitude INT,
PositionGuid UNIQUEIDENTIFIER
)
INSERT INTO #test VALUES (1,1,NEWID())
INSERT INTO #test VALUES (2,2,NEWID())
WITH XMLNAMESPACES ('http://www.w3.org/2003/05/soap-envelope' AS s, 'http://www.w3.org/2005/08/addressing' AS a)
SELECT
( SELECT '1' AS [a:Action/#mustUnderstand],
'http://tempuri.org/IGeocodeService/HereRouteMatchExtension' AS [a:Action]
FOR XML PATH (''), TYPE
),
'urn:uuid: some_messageID' AS 'a:MessageID',
( SELECT 'http://www.w3.org/2005/08/addressing/anonymous' AS [a:Address]
FOR XML PATH ('a:ReplyTo'), TYPE
),
( SELECT '1' AS [a:To/#mustUnderstand],
'ServiceURL' AS [a:To]
FOR XML PATH (''), TYPE
),
( SELECT '1' AS [a:Action/#mustUnderstand]
FOR XML PATH (''), TYPE
),
(SELECT Latitude AS 's:Latitude',
Longitude AS 's:Longitude',
PositionGuid AS 's:PositionGuid'
FROM #test
FOR XML PATH ('s:Body'), TYPE
)
FOR XML RAW ('s:Header'), ELEMENTS, ROOT('s:Envelope')
There are 2 problems in the code i generated:
1) reference URLS are in every sub-section and I would like to have just once;
2) Body tag is inside the header and it is supposed to go straight after it...
How can I achieve that? This is the result I am getting:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope" mustUnderstand="1">http://tempuri.org/IGeocodeService/HereRouteMatchExtension</a:Action>
<a:MessageID>urn:uuid: some_messageID</a:MessageID>
<a:ReplyTo xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope" mustUnderstand="1">ServiceURL</a:To>
<a:Action xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope" mustUnderstand="1" />
<s:Body xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Latitude>1</s:Latitude>
<s:Longitude>1</s:Longitude>
<s:PositionGuid>34BD8E91-8567-4D58-A18E-61C3FBBF5C8F</s:PositionGuid>
</s:Body>
</s:Header>
</s:Envelope>
Try that
DECLARE #test TABLE
(
Latitude INT,
Longitude INT,
PositionGuid UNIQUEIDENTIFIER
)
INSERT INTO #test VALUES (1,1,NEWID());
INSERT INTO #test VALUES (2,2,NEWID());
WITH XMLNAMESPACES ('http://www.w3.org/2003/05/soap-envelope' AS s, 'http://www.w3.org/2005/08/addressing' AS a)
SELECT
1 AS [s:Envelope/s:Header/a:Action/#s:mustUnderstand],
'http://tempuri.org/IGeocodeService/HereRouteMatchExtension' AS [s:Envelope/s:Header/a:Action],
'urn:uuid: some_messageID' AS [s:Envelope/s:Header/a:MessageID],
'http://www.w3.org/2005/08/addressing/anonymous' [s:Envelope/s:Header/a:ReplyTo/a:Address],
1 as [s:Envelope/s:Header/To/#s:mustUnderstand],
'ServiceURL' as [s:Envelope/s:Header/To],
Latitude as [s:Envelope/s:Body/s:Latitude],
Longitude as [s:Envelope/s:Body/s:Longitude],
PositionGuid as [s:Envelope/s:Body/s:PositionGuid]
FROM #test
FOR XML PATH('')
This way you produce two Envelope elements one for each row of the #table.
Edit:
I could not find a way to remove extra namespaces from child elements when generated with TYPE in a subquery with FOR XML PATH.
I came up with this solution which does not fully satisfy me:
DECLARE #test TABLE
(
Latitude INT,
Longitude INT,
PositionGuid UNIQUEIDENTIFIER,
x xml
)
DECLARE #x xml =
'<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IGeocodeService/HereRouteMatchExtension</a:Action>
<a:MessageID>urn:uuid: some_messageID</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">ServiceURL</a:To>
</s:Header>
<s:Body>
<HereRouteMatchExtension xmlns="http://tempuri.org/">
<vehicleTrace xmlns:b="http://schemas.datacontract.org/2004/07/FMCommonTypes.WCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<s:Latitude>2</s:Latitude>
<s:Longitude>2</s:Longitude>
<s:PositionGuid>577AF773-C7A8-4D65-82DA-37A15CC7611D</s:PositionGuid>
</vehicleTrace>
</HereRouteMatchExtension>
</s:Body>
</s:Envelope>'
INSERT INTO #test VALUES (10,10,NEWID(),#x);
INSERT INTO #test VALUES (20,20,NEWID(),#x);
UPDATE #test
SET x.modify('declare namespace s="http://www.w3.org/2003/05/soap-envelope";declare default element namespace "http://tempuri.org/";
replace value of (/s:Envelope/s:Body/HereRouteMatchExtension/vehicleTrace/s:Latitude/text())[1] with sql:column("Latitude")')
UPDATE #test
SET x.modify('declare namespace s="http://www.w3.org/2003/05/soap-envelope";declare default element namespace "http://tempuri.org/";
replace value of (/s:Envelope/s:Body/HereRouteMatchExtension/vehicleTrace/s:Longitude/text())[1] with sql:column("Longitude")')
UPDATE #test
SET x.modify('declare namespace s="http://www.w3.org/2003/05/soap-envelope";declare default element namespace "http://tempuri.org/";
replace value of (/s:Envelope/s:Body/HereRouteMatchExtension/vehicleTrace/s:PositionGuid/text())[1] with sql:column("PositionGuid")')
SELECT * FROM #test
and all at once with an insert:
DECLARE #test TABLE
(
Latitude INT,
Longitude INT,
PositionGuid UNIQUEIDENTIFIER,
x xml
)
DECLARE #x xml =
'<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IGeocodeService/HereRouteMatchExtension</a:Action>
<a:MessageID>urn:uuid: some_messageID</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">ServiceURL</a:To>
</s:Header>
<s:Body>
<HereRouteMatchExtension xmlns="http://tempuri.org/">
</HereRouteMatchExtension>
</s:Body>
</s:Envelope>'
INSERT INTO #test VALUES (10,10,NEWID(),#x);
INSERT INTO #test VALUES (20,20,NEWID(),#x);
UPDATE #test
SET x.modify('declare namespace s="http://www.w3.org/2003/05/soap-envelope";declare default element namespace "http://tempuri.org/";
insert <vehicleTrace xmlns:b="http://schemas.datacontract.org/2004/07/FMCommonTypes.WCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><s:Latitude>{sql:column("Latitude")}</s:Latitude>
<s:Longitude>{sql:column("Longitude")}</s:Longitude>
<s:PositionGuid>{sql:column("PositionGuid")}</s:PositionGuid></vehicleTrace> into (/s:Envelope/s:Body/HereRouteMatchExtension)[1]')
SELECT * FROM #test

Variable usage in XML query

Please check below query.
declare #xmlRoot as xml
set #xmlRoot= '<Root>
<table1 col1="2012-03-02T16:42:55.777">
<table2Array>
<Table2 col2="abc">
</Table2>
<Table2 col2="def">
</Table2>
</table2Array>
</table1>
<table1 col1="2012-03-02T17:42:55.777">
<table2Array>
<Table2 col2="abc1">
</Table2>
<Table2 col2="def1">
</Table2>
</table2Array>
</table1>
</Root>'
declare #a as varchar(1)
set #a= '1'
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[1]/table2Array/Table2' ) AS T(item);
--The above query return expected output
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[*[local-name()=sql:variable("#a")]]/table2Array/Table2' )
AS T(item);
--The above query doesn't return expected output
what am I doing wrong here?
Since I dont have a key value in parent node to identify child node. I have to parse through index.
This worked for me:
DECLARE #a INT; -- data type is probably important!
SET #a = 1;
SELECT col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[sql:variable("#a")]/table2Array/Table2') AS T(item);

Resources