Run the loop to the number of xml elements in SQLServer - sql-server

I have an xml and I want an operation to be repeated in the query for each xml element.
Please help me
DECLARE #PersonelIds XML
= '<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPersonelsIdsVm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PersonelsIdsVm>
<PersonelId>c2aadd5d-209d-ec11-8f3c-b42e99ed152b</PersonelId>
</PersonelsIdsVm>
<PersonelsIdsVm>
<PersonelId>0d197668-209d-ec11-8f3c-b42e99ed152b</PersonelId>
</PersonelsIdsVm>
</ArrayOfPersonelsIdsVm>';
DECLARE #FromDate DATE = CONVERT(DATE, '2022/05/1');
DECLARE #Days INT = 0;
--XML Initialize
DECLARE #handler INT;
EXEC sys.sp_xml_preparedocument #handler OUT, #PersonelIds;
DECLARE #i INT =0
WHILE 'xml count' <= #i
BEGIN
-- 'my code ... for example : print PersonelId'
END
Help me find the elements in order in the loop

Microsoft proprietary OPENXML() and its companions sp_xml_preparedocument and sp_xml_removedocument are kept just for backward compatibility with the obsolete SQL Server 2000. Their use is diminished just to very few fringe cases.
Starting from SQL Server 2005 onwards, it is strongly recommended to re-write your SQL and switch it to XQuery.
Also, OPENXML() cannot take advantage of XML indexes while XQuery methods can.
SQL
DECLARE #PersonelIds XML =
'<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPersonelsIdsVm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PersonelsIdsVm>
<PersonelId>c2aadd5d-209d-ec11-8f3c-b42e99ed152b</PersonelId>
</PersonelsIdsVm>
<PersonelsIdsVm>
<PersonelId>0d197668-209d-ec11-8f3c-b42e99ed152b</PersonelId>
</PersonelsIdsVm>
</ArrayOfPersonelsIdsVm>';
SELECT c.value('(PersonelId/text())[1]', 'UNIQUEIDENTIFIER') AS PersonelId
FROM #PersonelIds.nodes('/ArrayOfPersonelsIdsVm/PersonelsIdsVm') AS t(c);
Output
+--------------------------------------+
| PersonelId |
+--------------------------------------+
| C2AADD5D-209D-EC11-8F3C-B42E99ED152B |
| 0D197668-209D-EC11-8F3C-B42E99ED152B |
+--------------------------------------+

Related

How to split string from XML content and get the required value

Hello all I am converting an xml content and inserting it to a table variable as follows
DECLARE #iDoc int
SET #XMLData = '<NewDataSet>
<Table>
<DataId>2324205.3933251.7336404</DataId>
<IsVisible>true</IsVisible>
<Notes />
<Marks>85.5</Marks>
</Table>
</NewDataSet>'
EXEC sp_xml_preparedocument #idoc OUTPUT, #XMLData
SELECT DataId FROM OPENXML(#idoc, 'NewDataSet/Table', 1)
WITH (DataId NVARCHAR(250) 'DataId')```
I would like to split the dot value and retrieve the the first value, can some one help me how to can I do that with in XML
IsVisible is a bit filed, Marks is deicmal like that I will have
Starting from SQL Server 2005 onwards, it is better to use XQuery language, based on the w3c standards, while dealing with the XML data type.
Microsoft proprietary OPENXML and its companions sp_xml_preparedocument and sp_xml_removedocument are kept just for backward compatibility with the obsolete SQL
Server 2000. Their use is diminished just to very few fringe cases.
It is strongly recommended to re-write your SQL and switch it to XQuery.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, Notes NVARCHAR(MAX));
INSERT INTO #tbl (Notes) VALUES
(N'<NewDataSet>
<Table>
<DataId>2324205.3933251.7336404</DataId>
</Table>
</NewDataSet>');
-- DDL and sample data population, end
WITH rs AS
(
SELECT *
, TRY_CAST(Notes AS XML).value('(/NewDataSet/Table/DataId/text())[1]', 'VARCHAR(MAX)') AS x
FROM #tbl
)
SELECT *
, LEFT(x, CHARINDEX('.', x) - 1) AS [After]
, PARSENAME(x, 3) AS [After2]
FROM rs;
Output
+-------------------------+---------+
| Before | After |
+-------------------------+---------+
| 2324205.3933251.7336404 | 2324205 |
+-------------------------+---------+

T-SQL parse XML data into single line

I have XML saved into a column in a table as type nvarchar. Now I need to parse data from that xml. I do
SELECT
CONVERT(XML, columnX).value('(chatTranscript/message/msgText/text())[1]', 'nvarchar(max)')
as chat but I get only first value. How do I extract all into single line? XML can be long, depends on chat length.
I need to get userNick and then msgText and loop it till the end. Something like this:
userX:Hello<>userY:How are you;
XML:
<?xml version="1.0"?>
<chatTranscript startAt="2020-07-30T11:00:12Z" sessionId="......">
<newParty userId="......" timeShift="0" visibility="ALL" eventId="1">
<userInfo personId="" userNick="userX"/>
</newParty>
<message userId="..." timeShift="12" visibility="ALL" eventId="9">
<msgText msgType="text">Hello</msgText>
</message>
<newParty userId="..." timeShift="15" visibility="ALL" eventId="10">
<userInfo userNick="userY"/>
</newParty>
<message userId="..." timeShift="29" visibility="ALL" eventId="12">
<msgText treatAs="NORMAL">how are you?</msgText>
</message>
<partyLeft userId="..." timeShift="36" visibility="ALL" eventId="13" askerId="...">
<reason code="1">left with request to close if no agents</reason>
</partyLeft>
<partyLeft userId="..." timeShift="36" visibility="ALL" eventId="14" askerId="...">
<reason code="4">removed by other party</reason>
</partyLeft>
</chatTranscript>
You need code to do this cleanly. Trying to do what you are asking will be super messy T-SQL. I'd recommend parsing the xml in code to generate what you want based on that xml. You could also create a CLR function using code so that you can create a SQL function to do this. You can do some amazing things with XQuery and T-SQL, but sometimes it just gets to messy. For xml manipulation all within the database, CLR functions are perfect.
Here is my solution:
ALTER FUNCTION [dbo].[fn_parse_chat_xml] (#xml XML)
RETURNS NVARCHAR(MAX)
BEGIN
DECLARE #n INT, #content NVARCHAR(MAX), #userId NVARCHAR(200), #userNick1 NVARCHAR(200), #userNick2 NVARCHAR(200), #userNickX NVARCHAR(200)
SET #n = 1
SET #userId = #xml.value('(chatTranscript/newParty/#userId)[1]', 'nvarchar(max)')
SET #userNick1 = #xml.value('(chatTranscript/newParty/userInfo/#userNick)[1]', 'nvarchar(max)')
SET #userNick2 = #xml.value('(chatTranscript/newParty/userInfo/#userNick)[2]', 'nvarchar(max)')
WHILE DATALENGTH(#xml.value('(chatTranscript/message/msgText/text())[sql:variable("#n")][1]', 'nvarchar(max)'))>0
BEGIN
IF #userId = #xml.value('(chatTranscript/message/#userId)[sql:variable("#n")][1]', 'nvarchar(max)')
SET #userNickX = #userNick1
else
SET #userNickX = #userNick2
SET #content = concat(#content, ' <> ', #userNickX, ': ', #xml.value('(chatTranscript/message/msgText/text())[sql:variable("#n")][1]', 'nvarchar(max)'))
SET #n = #n + 1
END
RETURN #content
END

The XML parse error in MSSQL 2008R2

This is xml file:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.fleettracker.de/api/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:GetPositionsResponse>
<body>
<result>Found 1 vessels.</result>
<success>true</success>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9456159</ns1:imono>
<ns1:sid>780</ns1:sid>
<ns1:name>Trenta</ns1:name>
<ns1:charterShipName>Trenta</ns1:charterShipName>
<ns1:pasttrack>
<lat>1832900</lat>
<lon>7570400</lon>
<timestamp>2014-01-14T08:28:45Z</timestamp>
<orderNumber>0</orderNumber>
<sog>9.5</sog>
<cog>22</cog>
<hdg>22</hdg>
<eta>2014-01-15T12:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1224000</lat>
<lon>7188500</lon>
<timestamp>2014-01-11T08:07:45Z</timestamp>
<orderNumber>9</orderNumber>
<sog>7</sog>
<cog>39</cog>
<hdg>39</hdg>
<eta>2014-01-15T08:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
</shipsWithPositions>
</body>
</ns1:GetPositionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
And this is MsSql query:
EXEC sp_xml_preparedocument #idoc OUTPUT, #XML,'<root xmlns:ns1="http://www.fleettracker.de/api/1.0">'
--select #idoc
insert into Position (ImoNo, sid, VesselName, time, lat, lon, sog, cog, hdg, eta, NextPort)
SELECT *
FROM OPENXML (#idoc, '/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:GetPositionsResponse/body/shipsWithPositions', 2)
WITH (ImoNo numeric(8, 0) 'ns1:imono',
sid numeric(5, 0) 'ns1:sid',
VesselName nvarchar(20) 'ns1:name',
time datetime 'ns1:pasttrack/timestamp',
lat numeric(9, 2) 'ns1:pasttrack/lat',
lon numeric(9, 2) 'ns1:pasttrack/lon',
sog numeric(9, 2) 'ns1:pasttrack/sog',
cog numeric(9, 2) 'ns1:pasttrack/cog',
hdg numeric(9, 2) 'ns1:pasttrack/hdg',
eta datetime 'ns1:pasttrack/eta',
NextPort nvarchar(20) 'ns1:pasttrack/nextport'
);
EXEC sp_xml_removedocument #idoc
Does somebody knows why I am getting the XML parse error?!
-------------------------------------------------------------
-------------------------------------------------------------
The problem is the type of the #XML variable.
As the file header decalres 'UTF-8' coding, the document must be inside a varchar() variable:
declare #XML varchar(max) = 'your UTF-8 document'
If you decalre it as nvarchar(max) 'UTF-8' coding is not valid.
declare #XML nvarchar(max) = 'your UTF-8 document' -- This cannot be parsed!

SQL Server XML modify with no result

I have an xml document like
<xsd:form-definition ...>
<xsd:page ..></xsd:page>
<xsd:page ..></xsd:page>
...
</xsd:form-definition>
and I want to insert a new node in the first <xsd:page> element
DECLARE #res XML = '<Subject>English</Subject>'
SET #myXmlContent.modify('insert sql:variable("#res") as first into (/form-definition/page)[1]')
but nothing changes, why ?
The XQuery for your modify needs to declare and reference the xsd namespace:
SET #myXmlContent.modify('declare namespace xsd="..."; insert sql:variable("#res") as first into (/xsd:form-definition/xsd:page)[1]')
Try this one -
DECLARE #XML XML
SELECT #XML =
'<xsd_form-definition>
<xsd_page></xsd_page>
<xsd_page></xsd_page>
</xsd_form-definition>'
DECLARE #res XML = '<VehicleManufacturerID>abc</VehicleManufacturerID>'
SELECT #XML.modify('insert sql:variable("#res") into (xsd_form-definition/xsd_page)[1]')
SELECT #XML

SELECT node text values from xml document in TSQL OPENXML

I have a xml document I want to use to update values in a stored procedure. I can process the XML using OPENXML, but I'm confused about extracting the values I want. Each row in the xml is a product record and I want to create a variable for each property. Cell0 is the ID, Cell2 description etc
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row/cell',1)
with (Col1 varchar(29) 'text()')
Running the above query returns 1 record for each CELL in the xml. I want to be able to return 1 record per row with different columns for each cell, something like:-
Prod Description Qty
---------- -------------------- --------
1 BALSAMO DERMOSCENT 1.00
2 BAYTRIL 150 MG 1 CPDO 1.00
I'm using MSSQL 2008
I've come up with the following which does the job for me
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row',1)
with (pLineNo int 'cell[1]/text()',
pDesc varchar(50) 'cell[2]/text()',
pQty float 'cell[3]/text()',
pCost float 'cell[4]/text()',
pPvp float 'cell[5]/text()',
pTotal float 'cell[6]/text()',
pIva float 'cell[7]/text()',
pId int 'cell[8]/text()',
pnoFact varchar(5) 'cell[9]/text()')
Why use openxml on sql server 2008?
This is a better option (I used varchar(max) as the datatype, but enter whatever is applicable). Note you have to declare the variable as xml, not varchar.
SELECT
Row.Item.value('data(cell[1])', 'varchar(max)') As Prod,
Row.Item.value('data(cell[2])', 'varchar(max)') As Description,
Row.Item.value('data(cell[3])', 'varchar(max)') As Qty
FROM
#doc.nodes('//row') AS Row(Item)
Note: If you're doing this is a stored procedure you may have to include the following before the select statement:
SET ARITHABORT ON -- required for .nodes
If you must use openxml, at least clean it up when you're done:
exec sp_xml_removedocument #idoc

Resources