Converting rows to XML format in SQL Server - sql-server

I have requirement like below.
And ddl and dml script for above image is
CREATE TABLE #example
([CCP_DETAILS_SID] int, [ACCOUNT_GROWTH] int, [PRODUCT_GROWTH] int, [PROJECTION_SALES] numeric(22,6), [PROJECTION_UNITS] numeric(22,6), [PERIOD_SID] int)
;
INSERT INTO #example
([CCP_DETAILS_SID], [ACCOUNT_GROWTH], [PRODUCT_GROWTH], [PROJECTION_SALES], [PROJECTION_UNITS], [PERIOD_SID])
VALUES
(30001, 0, 0, 1505384.695, 18487.25251, 1801),
(30001, 0, 0, 1552809.983, 18695.75536, 1802),
(30001, 0, 0, 1595642.121, 18834.75725, 1803),
(30002, 0, 0, 10000.32, 18834.75725, 1801),
(30002, 0, 0, 1659124.98, 18834.75725, 1802),
(30002, 0, 0, 465859546.6, 18834.75725, 1803)
;
And i have to convert above results to xml format like below (Output).
ccp_details_sid xml_format_string
30001 <period>
<period_sid period_sid=1801>
<PROJECTION_SALES>1505384.695</PROJECTION_SALES>
<PROJECTION_UNITS>18487.25251<PROJECTION_UNITS>
<ACCOUNT_GROWTH>0</ACCOUNT_GROWTH>
<PRODUCT_GROWTH>0</PRODUCT_GROWTH>
</period_sid>
<period_sid period_sid=1802>
<PROJECTION_SALES>1552809.983</PROJECTION_SALES>
<PROJECTION_UNITS>18695.75536<PROJECTION_UNITS>
<ACCOUNT_GROWTH>0</ACCOUNT_GROWTH>
<PRODUCT_GROWTH>0</PRODUCT_GROWTH>
</period_sid>
<period_sid period_sid=1802>
<PROJECTION_SALES>1595642.121</PROJECTION_SALES>
<PROJECTION_UNITS>18834.75725<PROJECTION_UNITS>
<ACCOUNT_GROWTH>0</ACCOUNT_GROWTH>
<PRODUCT_GROWTH>0</PRODUCT_GROWTH>
</period_sid>
</period>
30002 Same like above
I am new to XML so couldn't able to do it quickly. I have used Marc_s solution with cross apply but can't able to achieve it.
Note: my major goal is, in above image if we see there are three records for single ccp_details_sid so I want to convert it as one row by using XML (mentioned above).

The following will work for you:
SELECT t.CCP_DETAILS_SID,
( SELECT PERIOD_SID AS [#period_sid],
x.PROJECTION_SALES,
x.PROJECTION_UNITS,
x.ACCOUNT_GROWTH,
x.PRODUCT_GROWTH
FROM #Example AS x
WHERE x.CCP_DETAILS_SID = t.CCP_DETAILS_SID
FOR XML PATH('period_sid'), TYPE, ROOT('period')
) AS xml_format_string
FROM #Example AS t
GROUP BY t.CCP_DETAILS_SID;
It essentially gets all your unique values for CCP_DETAILS_SID using:
SELECT t.CCP_DETAILS_SID
FROM #Example AS t
GROUP BY t.CCP_DETAILS_SID;
Then for each of these values uses the correlated subquery to form the XML. With the key points being:
Use # in front of the alias to create a property, e.g. AS [#period_sid]
Use PATH('period_sid') to name the container for each row
Use ROOT('period') to name the outer nodes.
Example on DBFiddle

Related

How do i extract xml data stored as data in a table column with a complex formed sql

I have XML data that comes in from a third party (so I cannot change the XML format) and gets stored in a table.
An example XML message format
<MedicalAidMessage xmlns="test.co.za/messaging" version="6.0">
<BenefitCheckResponseMessage>
<FinancialResponseDetails>
<FinancialResponseLines>
<FinancialResponseLine>
<LineIdentifier>1</LineIdentifier>
<RequestedAmount>1000</RequestedAmount>
<AmountToProvider>800</AmountToProvider>
<AmountToMember>0</AmountToMember>
<MemberLiableAmount>200</MemberLiableAmount>
<MemberNotLiableAmount>0</MemberNotLiableAmount>
<TariffCode>12345</TariffCode>
<LineResponseCodes>
<LineResponseCode>
<Sequence>1</Sequence>
<Code>274</Code>
<Description>We have not paid the amount claimed because the funds in the Medical Savings are used up.</Description>
<Type>Info</Type>
</LineResponseCode>
<LineResponseCode>
<Sequence>2</Sequence>
<Code>1239</Code>
<Description>We have applied a co-payment on this claim, in line with the member’s plan benefit for MRI and CT scans.</Description>
<Type>Info</Type>
</LineResponseCode>
</LineResponseCodes>
</FinancialResponseLine>
</FinancialResponseLines>
</FinancialResponseDetails>
</BenefitCheckResponseMessage>
</MedicalAidMessage>
I have tried the following code to get the Tariffcode, RequestedAmount, AmountToProvider and AmountToMember
DECLARE #XMLData XML = (select top 1 replace(br.xmlmessage,'<?xml version="1.0" encoding="UTF-8"?>','')
from benefitcheckresponse br
inner join benefitcheck bc on bc.id = br.BenefitCheckId
where bc.id =1562
order by bc.id desc)
SELECT
[TariffCode] = Node.Data.value('TariffCode', 'vacrhar(50)'),
[RequestedAmount] = Node.Data.value('RequestedAmount', 'float)'),
[AmountToProvider] = Node.Data.value('AmountToProvider', 'float)'),
[AmountToMember] = Node.Data.value('AmountToMember', 'float)')
FROM #XMLData.nodes('/*/FinancialResponseLine/') Node(Data)
The problem I am getting is that it is giving me the following error message
Msg 9341, Level 16, State 1, Line 12 XQuery [nodes()]: Syntax error
near '', expected a step expression.
How do I resolve that error?
How would I include the results for multiple lines when there multiple line responses?
How would I include the values from the sub nodes to the line response nodes?
You would be better off with syntax like this, if I am honest. I wasn't able to complete the ORDER BY clause for you, as I don't know what column to order by, but as you have a TOP (1) you need one. You also need to define your XML namespace, which you have not, which would have resulted in no rows being returned:
WITH XMLNAMESPACES(DEFAULT 'test.co.za/messaging')
SELECT TOP (1)
FRL.FRL.value('(TariffCode/text())[1]','int') AS TariffCode,
FRL.FRL.value('(RequestedAmount/text())[1]','int') AS RequestedAmount, --I doubt float is the correct data type here
FRL.FRL.value('(AmountToProvider/text())[1]','int') AS AmountToProvider, --I doubt float is the correct data type here
FRL.FRL.value('(AmountToMember/text())[1]','int') AS AmountToMember --I doubt float is the correct data type here
FROM dbo.benefitcheckresponse br
INNER JOIN benefitcheck bc ON bc.id = br.BenefitCheckId
CROSS APPLY br.xmlmessage.nodes('MedicalAidMessage/BenefitCheckResponseMessage/FinancialResponseDetails/FinancialResponseLines/FinancialResponseLine') FRL(FRL)
WHERE bc.id = 1562
ORDER BY {Column Name(s)};

SQL Server - fill NULL row values with certain logic

I've got a PIVOT table output in SQL Server Management Studio. Every row contains data that are either filled or NULL. I need to fill the NULL values according to the following logic:
If value is NULL, then go further to the left (in the row) and fill it with the closest value to the left.
If there are no values to the left, then use the closest value to the right to fill it.
Thanks for your kind help,
Dave
Since you have a limited number of columns, coalesce() should do the trick
Select Product
,Time1 = coalesce(Time1,Time2,Time3,Time4,Time5,Time10,Time15,Time20,Time30)
,Time2 = coalesce(Time2,Time3,Time4,Time5,Time10,Time15,Time20,Time30,Time1)
,Time3 = coalesce(Time3,Time4,Time5,Time10,Time15,Time20,Time30,Time1,Time2)
,Time4 = coalesce(Time4,Time5,Time10,Time15,Time20,Time30,Time1,Time2,Time3)
,Time5 = coalesce(Time5,Time10,Time15,Time20,Time30,Time1,Time2,Time3,Time4)
,Time10 = coalesce(Time10,Time15,Time20,Time30,Time1,Time2,Time3,Time4,Time5)
,Time15 = coalesce(Time15,Time20,Time30,Time1,Time2,Time3,Time4,Time5,Time10)
,Time20 = coalesce(Time20,Time30,Time1,Time2,Time3,Time4,Time5,Time10,Time15)
,Time30 = coalesce(Time30,Time1,Time2,Time3,Time4,Time5,Time10,Time15,Time20)
From ...
Pivot ...

Insert into clickhouse JSONEachRow nested

I have the following table
SET flatten_nested = 0;
CREATE TABLE test.hm
(
customDimensions Array(Nested(index Nullable(Int64), value Nullable(String)))
)
engine = Memory;
I am trying to insert into it with the following query:
INSERT INTO test.hm FORMAT JSONEachRow {"customDimensions": [{"index": 1, "value": 2}]}
But it fails with
Code: 130, e.displayText() = DB::Exception: Array does not start with '[' character: (while reading the value of key customDimensions): (at row 1) (version 21.8.4.51 (official build))
How to fix it and insert JSON into flatten_nested = 0 having multi lvl nested hierarchy?
Are you sure you need Array(Nested because it's two-dimensional array.
you can use select to get understanding what JSONEachRow CH expectes
insert into test.hm values([[(1,'test1'), (2,'test2')]]);
select * from test.hm format JSONEachRow;
{"customDimensions":[[["1","test1"],["2","test2"]]]}
I guess you really need Array(Tuple(index Nullable(Int64), value Nullable(String)))
And you can use JSONExtract
https://kb.altinity.com/altinity-kb-schema-design/altinity-kb-jsonasstring-and-mat.-view-as-json-parser/
https://kb.altinity.com/altinity-kb-queries-and-syntax/jsonextract-to-parse-many-attributes-at-a-time/
Or https://clickhouse.com/docs/en/guides/developer/working-with-json/json-semi-structured/#json-object-type

SQL Server - How to Use Merge Statement for Slowly Changing Dimension with More Than Two Conditions?

I am trying to implement Slowly Changing Dimension Type 2 through T-SQL but I can't figure out how to put a request to work.
Table columns: cpf, nome, telefone_update, endereco_insert
Basically the logic is: if the MATCH doesn't happen using cpf, then the record must be inserted; if the MATCH happens but only the telefone_update field has changed, there is no need to another record and I just want to UPDATE and override the values; if the MATCH happens but only the endereco_insert field has changed I want to add a new record and update the start and end dates.
What I have so far is:
insert into #dm_lucas_tst (
[cpf],
[nome],
[telefone_update],
[endereco_insert],
[dt_scd_start],
[dt_scd_end],
[nu_scd_version]
)
select [cpf],
[nome],
[telefone_update],
[endereco_insert],
cast(dateadd(month, datediff(month, 0, getdate()), 0) as date) as [dt_scd_start],
'2199-12-31' AS [dt_scd_end],
1 AS [nu_scd_version]
from (
merge edw.dim.dm_lucas_tst as Target
using edw.dim.stg_lucas_tst as Source
on Target.cpf = Source.cpf
when not matched by target
then
insert (
[cpf],
[nome],
[telefone_update],
[endereco_insert],
[dt_scd_start],
[dt_scd_end],
[nu_scd_version]
)
values (
Source.[cpf],
Source.[nome],
Source.[telefone_update],
Source.[endereco_insert],
cast(dateadd(month, datediff(month, 0, getdate()), 0) as date),
'2199-12-31',
1
)
when matched
and Source.telefone_update <> Target.telefone_update
and Target.dt_scd_end = '2199-12-31'
then
update set telefone_update = Source.telefone_update
output $ACTION ActionOut,
Source.[cpf],
Source.[nome],
Source.[telefone_update],
Source.[endereco_insert]
) AS MergeOut
where MergeOut.ActionOut = 'UPDATE';
But I don't think putting another WHEN MATCH AND ... will make that work.
Any suggestions?
Thanks in advance!
Accordingly to your description, I'm assuming that you need:
SCD Type 1 for column [telefone_update]
SCD Type 2 for column [endereco_insert]
I've used application SCD Merge Wizard to easily create described logic.
When I made tests for it - everything looks as expected, I guess.
I described the process on my blog - please have a look and tell me if that was exactly what you wanted?
https://sqlplayer.net/2018/01/scd-type-1-type-2-in-merge-statement/

Delete XML Child Node Element in SQL Server 2008

I have this xml, I want to insert it into temp table including the values inside <salesorpurchase> node in a single row. Is this possible?
Or can you tell me how to remove the <SalesOrPurchase> without removing its inner text`?
<ItemServiceRet>
<ListID>80000012-1302270176</ListID>
<EditSequence>1302270195</EditSequence>
<Name>2nd Floor Shop</Name>
<FullName>2nd Floor Shop</FullName>
<IsActive>true</IsActive>
<SalesOrPurchase>
<Price>0.00</Price>
<AccountRef>
<ListID>800000B3-1302260225</ListID>
<FullName>Rent Income:Rent income 2ND FL:2nd Floor Shops</FullName>
</AccountRef>
</SalesOrPurchase>
</ItemServiceRet>
<ItemServiceRet>
<ListID>80000002-1277187768</ListID>
<EditSequence>1463398389</EditSequence>
<Name>VAT 16%</Name>
<FullName>VAT 16%</FullName>
<IsActive>true</IsActive>
<SalesOrPurchase>
<PricePercent>16.00</PricePercent>
<AccountRef>
<ListID>6B0000-1224749077</ListID>
<FullName>Vat account</FullName>
</AccountRef>
</SalesOrPurchase>
</ItemServiceRet>
1) Try following query to insert data from every ItemServiceRet element into one row (in this case, because there are two ItemServiceRet elements two rows will be inserted):
DECLARE #XmlData XML = N'
<ItemServiceRet>
<ListID>80000012-1302270176</ListID>
<EditSequence>1302270195</EditSequence>
<Name>2nd Floor Shop</Name>
<FullName>2nd Floor Shop</FullName>
<IsActive>true</IsActive>
<SalesOrPurchase>
<Price>0.00</Price>
...
</ItemServiceRet>'
SELECT ListID = y.XmlCol.value('(ListID)[1]', 'VARCHAR(19)'),
EditSequence = y.XmlCol.value('(EditSequence)[1]', 'INT'),
-- ...
Price = y.XmlCol.value('(SalesOrPurchase/Price)[1]', 'NUMERIC(9,2)'),
PricePercent = y.XmlCol.value('(SalesOrPurchase/PricePercent)[1]', 'NUMERIC(9,2)'),
ListID2 = y.XmlCol.value('(SalesOrPurchase/AccountRef/ListID)[1]', 'VARCHAR(19)')
INTO #Items
FROM (VALUES (#XmlData)) x(XmlCol)/*or #XmlData.nodes*/
CROSS APPLY x.XmlCol.nodes(N'ItemServiceRet') y(XmlCol)
/*
Results:
(2 row(s) affected)
*/
This solution is using two XML methods: nodes (shred XML into many rows) and value (it extract one value from current node/element).
2) Second solution is "deleting" the SalesOrderHeader node thus:
SELECT x.XmlCol.query('
for $i in (ItemServiceRet)
return
<ItemServiceRet>
<ListID>{$i/ListID/text()}</ListID>
<IsActive>{$i/IsActive/text()}</IsActive>
<Price>{$i/SalesOrPurchase/Price/text()}</Price>
<PricePercent>{$i/SalesOrPurchase/PricePercent/text()}</PricePercent>
<AccountRef>
<ListID>{$i/SalesOrPurchase/AccountRef/ListID/text()}</ListID>
<FullName>{$i/SalesOrPurchase/AccountRef/FullName/text()}</FullName>
</AccountRef>
</ItemServiceRet>
') AS NewXmlCol
FROM (SELECT (#XmlData)) x(XmlCol)

Resources