SQL SERVER XQuery node processing problem - sql-server

I have to process data from xml by date of operation, simulating day by day, during 4th months process in the data base with a XML file that looks like this:
<Operaciones_por_Dia>
<OperacionDia fecha="2020-01-30">
<PagoRecibo TipoRecibo="5" NumFinca="9782331"/>
<PagoRecibo TipoRecibo="5" NumFinca="6696849"/>
<TransConsumo id="1" LecturaM3="325" descripcion="Cobro Mensual" NumFinca="3336538"/>
<TransConsumo id="3" LecturaM3="40" descripcion="Lectura errĂ³nea" NumFinca="2425954"/>
</OperacionDia>
<OperacionDia fecha="2020-04-08">
<PagoRecibo TipoRecibo="7" NumFinca="1423800"/>
<PagoRecibo TipoRecibo="7" NumFinca="1393022"/>
<TransConsumo id="2" LecturaM3="22" descripcion="Reclamo de cliente" NumFinca="2101885"/>
</OperacionDia>
</Operaciones_por_Dia>
When I prepare the data, I store all the dates from XML into a variable table and then I iterate from the min date throught the max date (#FechaOperacion has stored that date), then I have to process the node data where the date match with the date from xml node .
I try it doing this, but #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE')
don't match well, the correct data that I have to compare is Fecha with #FechaOperacion, but I don't know how can I get that value with Xquery.
INSERT #PagosHoy (NumFinca, TipoRecibo, Fecha)
select ph.value('#NumFinca', 'INT')
, ph.value('#TipoRecibo', 'INT')
, ph.value('../#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') AS t(ph)
where #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE') = #FechaOperacion
With the following code it work well, but I want to know how to do it like the other way.
INSERT INTO #PagosHoy(NumFinca,TipoRecibo,Fecha)
SELECT [NumFinca],[TipoRecibo],[fechaDeIngreso]
FROM OPENXML (#hdoc, 'Operaciones_por_Dia/OperacionDia/PagoRecibo',1)
WITH ( [NumFinca] VARCHAR(30) '#NumFinca',
[TipoRecibo] INT '#TipoRecibo',
[fechaDeIngreso] VARCHAR(100) '../#fecha')
WHERE [fechaDeIngreso] = #FechaOperacion
EXEC spProcesaPagos #PagosHoy

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. It is strongly recommended to re-write your SQL and switch it to XQuery.
Here is another method similar to what is proposed by #AlwaysLearning, but more simple.
It is using XPath predicate instead of WHERE clause.
SQL
-- DDL and sample data population, start
DECLARE #PagosHoy TABLE (ID INT IDENTITY PRIMARY KEY, NumFinca INT, TipoRecibo INT, Fecha DATE);
DECLARE #xml XML =
N'<Operaciones_por_Dia>
<OperacionDia fecha="2020-01-30">
<PagoRecibo TipoRecibo="5" NumFinca="9782331"/>
<PagoRecibo TipoRecibo="5" NumFinca="6696849"/>
<TransConsumo id="1" LecturaM3="325" descripcion="Cobro Mensual"
NumFinca="3336538"/>
<TransConsumo id="3" LecturaM3="40" descripcion="Lectura errĂ³nea"
NumFinca="2425954"/>
</OperacionDia>
<OperacionDia fecha="2020-04-08">
<PagoRecibo TipoRecibo="7" NumFinca="1423800"/>
<PagoRecibo TipoRecibo="7" NumFinca="1393022"/>
<TransConsumo id="2" LecturaM3="22" descripcion="Reclamo de cliente"
NumFinca="2101885"/>
</OperacionDia>
</Operaciones_por_Dia>';
-- DDL and sample data population, end
DECLARE #FechaOperacion DATE = '2020-01-30';
INSERT #PagosHoy (NumFinca, TipoRecibo, Fecha)
SELECT c.value('#NumFinca', 'INT')
, c.value('#TipoRecibo', 'INT')
, #FechaOperacion AS FechaOperacion
FROM #xml.nodes('/Operaciones_por_Dia/OperacionDia[#fecha eq sql:variable("#FechaOperacion")]/PagoRecibo') AS t(c)
-- test
SELECT * FROM #PagosHoy;
Output
+----+----------+------------+------------+
| ID | NumFinca | TipoRecibo | Fecha |
+----+----------+------------+------------+
| 1 | 9782331 | 5 | 2020-01-30 |
| 2 | 6696849 | 5 | 2020-01-30 |
+----+----------+------------+------------+

The where clause in your first example queries /Operaciones_por_Dia/OperacionDia/#fecha[1] independently of the nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') query so will always be comparing #FechaOperacion against the first date attribute - which is 2020-01-30 in this document.
declare #FechaOperacion date = convert(date, '2020-01-30', 120);
select
ph.value('#NumFinca', 'INT'),
ph.value('#TipoRecibo', 'INT'),
ph.value('../#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') AS t(ph)
where #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE') = #FechaOperacion
Depending on the value of #FechaOperacion it will either return all rows in the document (when it matches) or now rows at all (when it doesn't)...
(No column name) (No column name) (No column name)
---------------- ---------------- ----------------
9782331 5 2020-01-30
6696849 5 2020-01-30
1423800 7 2020-04-08
1393022 7 2020-04-08
The solution is to query for nodes('/Operaciones_por_Dia/OperacionDia') and then use a cross apply to query the PagoRecibo child nodes.
declare #FechaOperacion date = convert(date, '2020-01-30', 120);
select
ph.value('#NumFinca', 'INT'),
ph.value('#TipoRecibo', 'INT'),
od.value('#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia') AS s(od)
cross apply s.od.nodes('PagoRecibo') AS t(ph)
where s.od.value('#fecha', 'DATE') = #FechaOperacion
Which returns...
(No column name) (No column name) (No column name)
---------------- ---------------- ----------------
9782331 5 2020-01-30
6696849 5 2020-01-30

Related

Split XML field into multiple delimited values - SQL

I have some XML content in a single field; I want to split each xml field in multiple rows.
The XML is something like that:
<env>
<id>id1<\id>
<DailyProperties>
<date>01/01/2022<\date>
<value>1<\value>
<\DailyProperties>
<DailyProperties>
<date>05/05/2022<\date>
<value>2<\value>
<\DailyProperties>
<\env>
I want to put everything in a table as:
ID DATE VALUE
id1 01/01/2022 1
id1 05/05/2022 2
For now I managed to parse the xml value, and I have found something online to get a string into multiple rows (like this), but my string should have some kind of delimiter. I did this:
SELECT
ID,
XMLDATA.X.query('/env/DailyProperties/date').value('.', 'varchar(100)') as r_date,
XMLDATA.X.query('/env/DailyProperties/value').value('.', 'varchar(100)') as r_value
from tableX
outer apply xmlData.nodes('.') as XMLDATA(X)
WHERE ID = 'id1'
but I get all values without a delimiter, as such:
01/10/202202/10/202203/10/202204/10/202205/10/202206/10/202207/10/202208/10/202209/10/202210/10/2022
Or, as in my example:
ID R_DATE R_VALUE
id01 01/01/202205/05/2022 12
I have found out that XQuery has a last() function that return the last value parsed; in my xml example it will return only 05/05/2022, so it should exists something for address the adding of a delimiter. The number of rows could vary, as it could vary the number of days of which I have a value.
Please try the following solution.
I had to fix your XML to make it well-formed.
SQL
DECLARE #tbl TABLE (id INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO #tbl (xmldata) VALUES
(N'<env>
<id>id1</id>
<DailyProperties>
<date>01/01/2022</date>
<value>1</value>
</DailyProperties>
<DailyProperties>
<date>05/05/2022</date>
<value>2</value>
</DailyProperties>
</env>');
SELECT p.value('(id/text())[1]','VARCHAR(20)') AS id
, c.value('(date/text())[1]','VARCHAR(10)') AS [date]
, c.value('(value/text())[1]','INT') AS [value]
FROM #tbl
CROSS APPLY xmldata.nodes('/env') AS t1(p)
OUTER APPLY t1.p.nodes('DailyProperties') AS t2(c);
Output
id
date
value
id1
01/01/2022
1
id1
05/05/2022
2
Yitzhak beat me to it by 2 min. Nonetheless, here's what I have:
--==== XML Data:
DECLARE #xml XML =
'<env>
<id>id1</id>
<DailyProperties>
<date>01/01/2022</date>
<value>1</value>
</DailyProperties>
<DailyProperties>
<date>05/05/2022</date>
<value>2</value>
</DailyProperties>
</env>';
--==== Solution:
SELECT
ID = ff2.xx.value('(text())[1]','varchar(20)'),
[Date] = ff.xx.value('(date/text())[1]', 'date'),
[Value] = ff.xx.value('(value/text())[1]', 'int')
FROM (VALUES(#xml)) AS f(X)
CROSS APPLY f.X.nodes('env/DailyProperties') AS ff(xx)
CROSS APPLY f.X.nodes('env/id') AS ff2(xx);
Returns:
ID Date Value
-------------------- ---------- -----------
id1 2022-01-01 1
id1 2022-05-05 2

Can SQL Server look ahead into XML tags and/or count them?

I have XML that looks like this:
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png" />
</channel>
Note the 5 XML elements all named display-name.
Here is the SQL Server code to insert the data:
SELECT *
INTO channels
FROM OPENXML (#hdoc, '/tv/channel', 2)
WITH (
id varchar(50) '#id',
chan1 varchar(50) 'display-name[1]',
chan2 varchar(50) 'display-name[2]',
chan3 varchar(50) 'display-name[3]',
chan4 float 'display-name[4]',
chan5 varchar(50) 'display-name[5]',
icon varchar(100)
)
The values of each tag is the same for all occurrences of the <channel> element, so use of the data in the database is not affected.
However, here is the XML in a new upgrade:
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png" />
</channel>
Note that the 2nd XML element is removed (presumably because it's identical to the 1st one). But when they are different, both elements appear.
So now I can't tell what's in the subsequent elements.
My question is, can SQL Server count the display-name elements, or look at and test the data values in the elements?
I tried this test and it works:
select iif(isnull(null, -1) = -1, 'null', 'not null')
But this code:
chan2 varchar(50) iif(isnull('display-name[5]', -1) = -1, 'display-name[2]', 'display-name[1]'),
Gives a syntax error.
If this can work, then the same format will be used for the columns chan2 through chan5. E.g, chan3 will be assigned to either index 3 or 2 of display-name, etc.
Suggestions?
Regardless of which xml format is encountered (whether there are 4 or 5 display-name elements), the result should look like this:
id chan1 chan2 chan3 chan4 chan5
EPG123.11331.schedulesdirect.org WCBS WCBS 2 WCBS 2 CBS
There will be cases where chan1 is different from chan2. But I can't tell when that will happen.
Here is the code that is causing a syntax error in the INTO clause.
DECLARE #x xml
SELECT #x = P
FROM OPENROWSET (BULK 'C:\projects\epg123new.xml', SINGLE_BLOB) AS tv(P)
DECLARE #hdoc int
EXEC sp_xml_preparedocument #hdoc OUTPUT, #x
insert into channels (id, chan1, chan2, chan3, chan4, chan5, icon)
SELECT
channel.value('(#id)[1]', 'varchar(50)') id,
channel.value('(display-name)[1]','varchar(50)') chan1,
channel.value('(display-name[(if(count(../display-name) = 4) then 1 else 2)])[1]','varchar(50)') chan2,
channel.value('(display-name[(if(count(../display-name) = 4) then 2 else 3)])[1]','varchar(50)') chan3 ,
channel.value('(display-name[(if(count(../display-name) = 4) then 3 else 4)])[1]','float') chan4,
channel.value('(display-name[(if(count(../display-name) = 4) then 4 else 5)])[1]', 'varchar(50)') chan5,
channel.value('(icon/#src)[1]','varchar(100)') icon
INTO channels
FROM #hdoc.nodes('/channel') x(channel)
EXEC sp_xml_removedocument #hdoc
go
Another method. Slightly more performant as it calculates number of <display-name> elements just once per channel. And eliminates a conditional logic with the if/else statement.
All credit goes to #Charlieface. I just optimized his solution.
SQL
DECLARE #xml XML =
N'<tv>
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png"/>
</channel>
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png"/>
</channel>
</tv>';
--INSERT INTO channels (...)
SELECT
c.value('(#id)[1]', 'varchar(50)') id,
c.value('(display-name/text())[1]','varchar(50)') chan1,
c.value('(display-name[sql:column("seq.pos") - 3]/text())[1]','varchar(50)') chan2,
c.value('(display-name[sql:column("seq.pos") - 2]/text())[1]','varchar(50)') chan3 ,
c.value('(display-name[sql:column("seq.pos") - 1]/text())[1]','varchar(50)') chan4,
c.value('(display-name[sql:column("seq.pos")]/text())[1]', 'varchar(50)') chan5,
c.value('(icon/#src)[1]','varchar(100)') icon
--, seq.pos
FROM #xml.nodes('/tv/channel') AS t(c)
CROSS APPLY (SELECT t.c.value('count(./display-name)','INT') AS pos
) AS seq;
Output
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
| id | chan1 | chan2 | chan3 | chan4 | chan5 | icon |
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
| EPG123.11331.schedulesdirect.org | WCBS | WCBS | 2 WCBS | 2 | CBS | https://schedulesdirect-api20141201.s3. ... w_270h.png |
| EPG123.11331.schedulesdirect.org | WCBS | WCBS | 2 WCBS | 2 | CBS | https://schedulesdirect-api20141201.s3. ... w_270h.png |
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
OPENXML has been somewhat deprecated in favour of XQuery functions such as .nodes() and .value().
In this example, I have used the variable directly in the FROM. If you have an XML table column, you should use CROSS APPLY.
.nodes breaks out the XML into rows, the example you gave does not have tv as the root node.
.value requires a single value result, best practice is to enclose the whole XQuery in brackets, followed by [1] to get the first node.
text() gets you the actual value of a node, you don't technically need this, but it performs faster.
# is short-hand to refer to an attribute.
.. is short-hand to refer to the immediate parent node.
We use the predicate [(if(count(../display-name) = 4) then ... to get the particular node we want, we still need [1] because it is not a constant expression.
SELECT
channel.value('(#id)[1]', 'varchar(50)') id,
channel.value('(display-name/text())[1]','varchar(50)') chan1,
channel.value('(display-name[(if(count(../display-name) = 4) then 1 else 2)]/text())[1]','varchar(50)') chan2,
channel.value('(display-name[(if(count(../display-name) = 4) then 2 else 3)]/text())[1]','varchar(50)') chan3 ,
channel.value('(display-name[(if(count(../display-name) = 4) then 3 else 4)]/text())[1]','float') chan4,
channel.value('(display-name[(if(count(../display-name) = 4) then 4 else 5)]/text())[1]', 'varchar(50)') chan5,
channel.value('(icon/#src)[1]','varchar(100)') icon
INTO channels
FROM #hdoc.nodes('/channel') x(channel)

SQL Cast String field to XML slow

I have a table in MS SQL with a field that contain a string that rappresent an XML like this:
< Root >
< Value_Tab2 ID = "182" >
< Value_Tab3 >
< Value > 1219 </ Value >
</ Value_Tab3 >
</ Value_Tab2 >
< Value_Tab2 ID = "187" >
< Value_Tab3 >
< Value > 3192 </ Value >
</ Value_Tab3 >
</ Value_Tab2 >
</ Root >
I can query this field casting string as XML but, with 50000 rows of MyTable1, query take 7-8 second. The query I use is this:
SELECT MT1.ID
,cast(MT1.StringXml as xml).value('(Root/Value_Tab2/#ID)[1]', 'INT') AS MT1_ID1
,cast(MT1.StringXml as xml).value('(Root/Value_Tab2/#ID)[2]', 'INT') AS MT1_ID2
,cast(MT1.StringXml as xml).value('(Root/Value_Tab2/Value_Tab3/Value)[1]', 'INT') AS VALUE_ID1
,cast(MT1.StringXml as xml).value('(Root/Value_Tab2/Value_Tab3/Value)[2]', 'INT') AS VALUE_ID2
FROM MyTable1 MT1 with (NOLOCK)
WHERE MT1.Published = 1
StringXML is MyTable1 filed as nvarchar(MAX); is this the correct way to query a string field and cast as XML?
Thare are other way or workaround to improve query performance?
What you need to do is called XML shredding.
Please try the following.
As #PanagiotisKanavos already pointed out, the CAST operation is done just once. All XPath expressions are optimized for performance.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, Published BIT, StringXml NVARCHAR(MAX));
INSERT INTO #tbl (Published, StringXml) VALUES
(1, N'<Root>
<Value_Tab2 ID="182">
<Value_Tab3>
<Value>1219</Value>
</Value_Tab3>
</Value_Tab2>
<Value_Tab2 ID="187">
<Value_Tab3>
<Value>3192</Value>
</Value_Tab3>
</Value_Tab2>
</Root>');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID, Published
, TRY_CAST(StringXml AS XML) AS xmldata
FROM #tbl
)
SELECT ID, c.value('Value_Tab2[1]/#ID', 'INT') AS MT1_ID1
, c.value('Value_Tab2[2]/#ID', 'INT') AS MT1_ID2
, c.value('(Value_Tab2[1]/Value_Tab3/Value/text())[1]', 'INT') AS VALUE_ID1
, c.value('(Value_Tab2[2]/Value_Tab3/Value/text())[1]', 'INT') AS VALUE_ID2
FROM rs
CROSS APPLY xmldata.nodes('/Root') AS t(c)
WHERE rs.Published = 1;
Output
+----+---------+---------+-----------+-----------+
| ID | MT1_ID1 | MT1_ID2 | VALUE_ID1 | VALUE_ID2 |
+----+---------+---------+-----------+-----------+
| 1 | 182 | 187 | 1219 | 3192 |
+----+---------+---------+-----------+-----------+

Multi-row XML return in mssql

I have the below XML returned by the application. I am writing a query to insert all amounts in the XML to a table and expecting the values to return as 100, 99.09 but due to second row of same tab name, the 0 gets added to the Amount which returns me 1000 instead of 100. How to resolve this?
declare #s xml
set #s='<Transaction><Header><HeaderId>42</HeaderId><ProdOrderId>0</ProdOrderId><VoucherType>8448</VoucherType><DocNo>1</DocNo><Date>132384273</Date><Time>0</Time><CreatedDate>132384273</CreatedDate><CreatedTime>932124</CreatedTime><ModifiedDate>132384273</ModifiedDate><ModifiedTime>932124</ModifiedTime><Flags><UpdateInv>false</UpdateInv><UpdateFA>true</UpdateFA><CantEdit>false</CantEdit><Editing>true</Editing><FromWeb>false</FromWeb><Suspended>false</Suspended><Internal>false</Internal><Approved>true</Approved><PrintAfterSave>false</PrintAfterSave><UnsaveInv>false</UnsaveInv><RequestCrLimit>false</RequestCrLimit><SaveInThread>false</SaveInThread><TDSCertPrepared>false</TDSCertPrepared><TDSPaid>false</TDSPaid><PostCashEntry>false</PostCashEntry><FromTrigger>false</FromTrigger><CheckNegativeCash>true</CheckNegativeCash><CheckCreditLimit>true</CheckCreditLimit><CheckOverdueBills>true</CheckOverdueBills><CheckNegativeStock>true</CheckNegativeStock><CheckNegativeBudget>true</CheckNegativeBudget><PostingFromUI>false</PostingFromUI><CheckReorderLevel>true</CheckReorderLevel><Cancelled>false</Cancelled><AuthByHigherUps>false</AuthByHigherUps><CantPrint>false</CantPrint><Version>false</Version><FromUI>false</FromUI><NoLinkCheck>true</NoLinkCheck><NoBatchCheck>true</NoBatchCheck><FromPDC>false</FromPDC><AlreadyLoaded>false</AlreadyLoaded><WMSAllocated>false</WMSAllocated><Amended>false</Amended><DontAdjustBills>false</DontAdjustBills><ChequeReturn>false</ChequeReturn><TriggerRaised>false</TriggerRaised></Flags><EnteredBy>1</EnteredBy><ModifiedBy>1</ModifiedBy><LC>0</LC><PmtTerm>0</PmtTerm><PrintCount>0</PrintCount><ModifiedDiffLoc>false</ModifiedDiffLoc><EditingLocation>0</EditingLocation><SyncDateTime>0</SyncDateTime><ProductionSize>0</ProductionSize><Versions /><BillStatus>None</BillStatus><LinkStatus>Partial</LinkStatus><TriggerBaseHeaderId>0</TriggerBaseHeaderId><EmailCount>0</EmailCount><Source>Screen</Source><ProcessId>0</ProcessId><Net>0.0000000000</Net><TransNet>0</TransNet><RepostId>0</RepostId><AuthStatus>Authorized</AuthStatus></Header><HeaderExtra><IdNamePair><ID>67108916</ID><Name>sNarration</Name><Tag></Tag></IdNamePair></HeaderExtra><BodyData><TransBody><TransactionId>363</TransactionId><BodyId>363</BodyId><Sequence>0</Sequence><FATag>11</FATag><InvTag>0</InvTag><Code>4</Code><Book>65</Book><CurrencyId>7</CurrencyId><ExchangeRate>1.0000000000</ExchangeRate><LocalExchangeRate>0</LocalExchangeRate><Tags
/><BodyExtra><IdNamePair><ID>16779281</ID><Name>ExchngDiff</Name><Tag>5.120</Tag></IdNamePair></BodyExtra><Amounts><decimal>-100</decimal><decimal>0</decimal></Amounts><OriginalAmounts><decimal>-100</decimal><decimal>0</decimal></OriginalAmounts><Amount3>0</Amount3><TransAmount3>0</TransAmount3><OrigAmount>0</OrigAmount><DueDate>132384273</DueDate><RowType>Default</RowType><AuthStatus>Authorized</AuthStatus><Flags><SuspendFA>false</SuspendFA><SuspendInv>false</SuspendInv><SuspendBase>false</SuspendBase><SuspendLink>false</SuspendLink><Void>false</Void><ForexFlux>false</ForexFlux><BRS>false</BRS><PDC>false</PDC><TransferToPnL>false</TransferToPnL><InternalIIDST>false</InternalIIDST><ConfirmBins>false</ConfirmBins><FreeQty>false</FreeQty><PDCDisc>false</PDCDisc><SuspendRef>false</SuspendRef><SuspendReservation>false</SuspendReservation><FooterRow>false</FooterRow><PDCConverted>false</PDCConverted><UpdateFA>true</UpdateFA></Flags><LocalCurrencyData><CurrencyId>0</CurrencyId><ExchangeRate>1</ExchangeRate><Amounts><decimal>0.0000000000</decimal><decimal>0.0000000000</decimal></Amounts><Amount3>0</Amount3></LocalCurrencyData><MainBodyId>0</MainBodyId></TransBody><TransBody><TransactionId>364</TransactionId><BodyId>364</BodyId><Sequence>0</Sequence><FATag>11</FATag><InvTag>0</InvTag><Code>62</Code><Book>65</Book><CurrencyId>2</CurrencyId><ExchangeRate>3.6700000000</ExchangeRate><LocalExchangeRate>0</LocalExchangeRate><Tags
/><BodyExtra><IdNamePair><ID>16779281</ID><Name>ExchngDiff</Name><Tag>0</Tag></IdNamePair></BodyExtra>
<Amounts><decimal>99.09</decimal></Amounts><OriginalAmounts><decimal>27.00</decimal><decimal>0</decimal></OriginalAmounts><Amount3>0</Amount3><TransAmount3>0</TransAmount3><OrigAmount>0</OrigAmount><DueDate>132384273</DueDate><RowType>Default</RowType><AuthStatus>Authorized</AuthStatus><Flags><SuspendFA>false</SuspendFA><SuspendInv>false</SuspendInv><SuspendBase>false</SuspendBase><SuspendLink>false</SuspendLink><Void>false</Void><ForexFlux>false</ForexFlux><BRS>false</BRS><PDC>false</PDC><TransferToPnL>false</TransferToPnL><InternalIIDST>false</InternalIIDST><ConfirmBins>false</ConfirmBins><FreeQty>false</FreeQty><PDCDisc>false</PDCDisc><SuspendRef>false</SuspendRef><SuspendReservation>false</SuspendReservation><FooterRow>false</FooterRow><PDCConverted>false</PDCConverted><UpdateFA>true</UpdateFA></Flags><LocalCurrencyData><CurrencyId>0</CurrencyId><ExchangeRate>1</ExchangeRate><Amounts><decimal>0.0000000000</decimal><decimal>0.0000000000</decimal></Amounts><Amount3>0</Amount3></LocalCurrencyData><MainBodyId>0</MainBodyId></TransBody></BodyData></Transaction>'
declare #table table (Amount1 decimal(18,3))
insert into #table
select Node.Data.value('Amounts[1]', 'decimal(18,3)') as Amount
from #s.nodes('Transaction/BodyData/TransBody') Node(data)
select * from #table
PLease try the following T-SQL.
SQL
-- DDL and sample data population, start
DECLARE #s XML =
N'<Transaction>
<BodyData>
<TransBody>
<TransactionId>363</TransactionId>
<BodyId>363</BodyId>
<Sequence>0</Sequence>
<FATag>11</FATag>
<InvTag>0</InvTag>
<Code>4</Code>
<Book>65</Book>
<CurrencyId>7</CurrencyId>
<ExchangeRate>1.0000000000</ExchangeRate>
<LocalExchangeRate>0</LocalExchangeRate>
<Tags/>
<BodyExtra>
<IdNamePair>
<ID>16779281</ID>
<Name>ExchngDiff</Name>
<Tag>5.120</Tag>
</IdNamePair>
</BodyExtra>
<Amounts>
<decimal>-100</decimal>
<decimal>0</decimal>
</Amounts>
<OriginalAmounts>
<decimal>-100</decimal>
<decimal>0</decimal>
</OriginalAmounts>
<Amount3>0</Amount3>
<TransAmount3>0</TransAmount3>
<OrigAmount>0</OrigAmount>
<DueDate>132384273</DueDate>
<RowType>Default</RowType>
<AuthStatus>Authorized</AuthStatus>
<Flags>
<SuspendFA>false</SuspendFA>
<SuspendInv>false</SuspendInv>
<SuspendBase>false</SuspendBase>
<SuspendLink>false</SuspendLink>
<Void>false</Void>
<ForexFlux>false</ForexFlux>
<BRS>false</BRS>
<PDC>false</PDC>
<TransferToPnL>false</TransferToPnL>
<InternalIIDST>false</InternalIIDST>
<ConfirmBins>false</ConfirmBins>
<FreeQty>false</FreeQty>
<PDCDisc>false</PDCDisc>
<SuspendRef>false</SuspendRef>
<SuspendReservation>false</SuspendReservation>
<FooterRow>false</FooterRow>
<PDCConverted>false</PDCConverted>
<UpdateFA>true</UpdateFA>
</Flags>
<LocalCurrencyData>
<CurrencyId>0</CurrencyId>
<ExchangeRate>1</ExchangeRate>
<Amounts>
<decimal>0.0000000000</decimal>
<decimal>0.0000000000</decimal>
</Amounts>
<Amount3>0</Amount3>
</LocalCurrencyData>
<MainBodyId>0</MainBodyId>
</TransBody>
<TransBody>
<TransactionId>364</TransactionId>
<BodyId>364</BodyId>
<Sequence>0</Sequence>
<FATag>11</FATag>
<InvTag>0</InvTag>
<Code>62</Code>
<Book>65</Book>
<CurrencyId>2</CurrencyId>
<ExchangeRate>3.6700000000</ExchangeRate>
<LocalExchangeRate>0</LocalExchangeRate>
<Tags/>
<BodyExtra>
<IdNamePair>
<ID>16779281</ID>
<Name>ExchngDiff</Name>
<Tag>0</Tag>
</IdNamePair>
</BodyExtra>
<Amounts>
<decimal>99.09</decimal>
</Amounts>
<OriginalAmounts>
<decimal>27.00</decimal>
<decimal>0</decimal>
</OriginalAmounts>
<Amount3>0</Amount3>
<TransAmount3>0</TransAmount3>
<OrigAmount>0</OrigAmount>
<DueDate>132384273</DueDate>
<RowType>Default</RowType>
<AuthStatus>Authorized</AuthStatus>
<Flags>
<SuspendFA>false</SuspendFA>
<SuspendInv>false</SuspendInv>
<SuspendBase>false</SuspendBase>
<SuspendLink>false</SuspendLink>
<Void>false</Void>
<ForexFlux>false</ForexFlux>
<BRS>false</BRS>
<PDC>false</PDC>
<TransferToPnL>false</TransferToPnL>
<InternalIIDST>false</InternalIIDST>
<ConfirmBins>false</ConfirmBins>
<FreeQty>false</FreeQty>
<PDCDisc>false</PDCDisc>
<SuspendRef>false</SuspendRef>
<SuspendReservation>false</SuspendReservation>
<FooterRow>false</FooterRow>
<PDCConverted>false</PDCConverted>
<UpdateFA>true</UpdateFA>
</Flags>
<LocalCurrencyData>
<CurrencyId>0</CurrencyId>
<ExchangeRate>1</ExchangeRate>
<Amounts>
<decimal>0.0000000000</decimal>
<decimal>0.0000000000</decimal>
</Amounts>
<Amount3>0</Amount3>
</LocalCurrencyData>
<MainBodyId>0</MainBodyId>
</TransBody>
</BodyData>
</Transaction>';
DECLARE #table TABLE (ID INT IDENTITY PRIMARY KEY, Amount DECIMAL(18, 3));
-- DDL and sample data population, end
INSERT INTO #table
SELECT c.value('(./text())[1]', 'DECIMAL(18,2)') AS Amount
FROM #s.nodes('/Transaction/BodyData/TransBody/Amounts/decimal') t(c);
-- test
SELECT * FROM #table;
Output
+----+----------+
| ID | Amount |
+----+----------+
| 1 | -100.000 |
| 2 | 0.000 |
| 3 | 99.090 |
+----+----------+

how to add a xml node to xml DataColumn and return Xml Datacolumn as string in SQL

I have a table like below. ID column should be added into XML tag as Value with respective ID Column value and return as one XML tag
Ex:
Specification | ID
<car><Name>BMW</Name></car> | 245
<car><Name>Audi</Name></car> | 290
<car><Name>Skoda</Name></car>| 295
Output Should be as
| Specification |
| <car><Name>BMW</Name><ID>245</ID></car><car><Name>Audi</Name><ID>290</ID></car><car><Name>Audi</Name><ID>295</ID></car> |
You could use modify() and insert xml like this
DECLARE #SampleData AS TABLE
(
Specification xml,
Id int
)
INSERT INTO #SampleData
VALUES
('<car><Name>BMW</Name></car>', 245),
('<car><Name>Audi</Name></car>', 290),
('<car><Name>Skoda</Name></car>', 295)
Update #SampleData SET Specification.modify('insert <Id>{sql:column("sd.Id")}</Id> into (/car)[1]')
FROM #SampleData sd
SELECT sd.* FROM #SampleData sd
Demo link: Rextester
Some reference links:
modify xml
insert xml
this is one way I can think of. You can strip the value of the car name using the .node, and reconstruct the XML. Something like,
CREATE TABLE #Temp
(
Specification XML,
ID INT
)
INSERT #Temp ( Specification, ID )
VALUES
('<car><Name>BMW</Name></car>', 245),
('<car><Name>Audi</Name></car>', 290),
('<car><Name>Skoda</Name></car>', 295)
SELECT * FROM
(
SELECT
Val.value('(text())[1]', 'varchar(32)') AS Name,
T.ID
FROM #Temp AS T
CROSS APPLY Specification.nodes('car/Name') AS Id(Val)
) X
FOR XML PATH('Name'), ROOT ('Car')
DROP TABLE #Temp

Resources