How to convert xpath to xml with multiple nodes in SQL Server - sql-server

I have a XML like this, I save the XML values in a table with value and xpath in SQL Server, as a result I have a table as below of 2nd, I need to parse XML in to path and values, then I need to gain a proper XML, I mean same XML, using path and values? I couldn't do it, how can I do this?
<Root>
<Trans>
<PaymentID>
<ID1>ID1</ID1>
<ID2>ID2</ID2>
<ID3>ID3</ID3>
<ID4>ID4</ID4>
</PaymentID>
<Amt Ccy="USD">1500</Amt>
<From>
<Inst>
<Adr>12345</Adr>
</Inst>
</From>
<To>
<Inst>
<Adr>6789</Adr>
</Inst>
</To>
<Debt>
<Inf>
<Adr>012345</Adr>
</Inf>
</Debt>
<Cred>
<Inf>
<Adr>44444</Adr>
<PstlAdr>
<AdrLine>ADR3:</AdrLine>
<AdrLine>ADR2:</AdrLine>
<AdrLine>ADR1:</AdrLine>
</PstlAdr>
</Inf>
</Cred>
</Trans>
</Root>
Table :
Root/Trans/Cred/Inf/PstlAdr/AdrLine[3] 38 ADR1: Root/Trans/Cred/Inf/PstlAdr/AdrLine
Root/Trans/Cred/Inf/PstlAdr/AdrLine[2] 37 ADR2: Root/Trans/Cred/Inf/PstlAdr/AdrLine
Root/Trans/Cred/Inf/PstlAdr/AdrLine 36 ADR3: Root/Trans/Cred/Inf/PstlAdr/AdrLine
Root/Trans/Cred/Inf/Adr 35 44444 Root/Trans/Cred/Inf/Adr
Root/Trans/Debt/Inf/Adr 34 012345 Root/Trans/Debt/Inf/Adr
Root/Trans/To/Inst/Adr 33 6789 Root/Trans/To/Inst/Adr
Root/Trans/From/Inst/Adr 32 12345 Root/Trans/From/Inst/Adr
Root/Trans/Amt/#Ccy 31 USD Root/Trans/Amt/#Ccy
Root/Trans/PaymentID/ID4 30 ID4 Root/Trans/PaymentID/ID4
Root/Trans/PaymentID/ID3 29 ID3 Root/Trans/PaymentID/ID3
Root/Trans/PaymentID/ID2 28 ID2 Root/Trans/PaymentID/ID2
Root/Trans/PaymentID/ID1 27 ID1 Root/Trans/PaymentID/ID1
Root/Trans/Amt 10 1500 Root/Trans/Amt
Then I need to convert these values to XML, but I can not convert repetitive values to XML appropriately, I use stuff and xml path and I get the result as below:
<AdrLine>ADR3:ADR2:ADR1:</AdrLine>
</PstlAdr>
How can I convert XML to xpath then xpath to XML properly?

Related

MSSQL Parsing of XML that Contains same Sibling

I am trying to query a MSSQL database that has XML and parse it out.
here is the data:
<Root>
<Relatives>
<Relative>
<Relation>Father</Relation>
<BcAge>50</BcAge>
<BilatAge>0</BilatAge>
<OcAge>0</OcAge>
</Relative>
<Relative>
<Relation>Mother</Relation>
<BcAge>58</BcAge>
<BilatAge>0</BilatAge>
<OcAge>0</OcAge>
</Relative>
Here is the desired result
Relation BcAge BilatAge OcAge Relation BcAge BilatAge OcAge
Father 50 0 0 Mother 58 0 0
I doubt, that you really want to get your result side-by-side... This is no valid resultset, your column names are duplicated.
If you really need it this way you might do this:
DECLARE #xml XML=
N'<Root>
<Relatives>
<Relative>
<Relation>Father</Relation>
<BcAge>50</BcAge>
<BilatAge>0</BilatAge>
<OcAge>0</OcAge>
</Relative>
<Relative>
<Relation>Mother</Relation>
<BcAge>58</BcAge>
<BilatAge>0</BilatAge>
<OcAge>0</OcAge>
</Relative>
</Relatives>
</Root>';
The query will use a CTE to get the Father and the Mother with an XQuery-predicate
WITH Parents AS
(
SELECT #xml.query(N'/Root/Relatives/Relative[(Relation/text())[1]="Father"]') AS Father
,#xml.query(N'/Root/Relatives/Relative[(Relation/text())[1]="Mother"]') AS Mother
)
SELECT 'Father' AS F_Relation
,Father.value(N'(/Relative/BcAge)[1]',N'int') AS F_BcAge
,Father.value(N'(/Relative/BilatAge)[1]',N'int') AS F_BilatAge
,Father.value(N'(/Relative/OcAge)[1]',N'int') AS F_OcAge
,'Mother' AS M_Relation
,Mother.value(N'(/Relative/BcAge)[1]',N'int') AS M_BcAge
,Mother.value(N'(/Relative/BilatAge)[1]',N'int') AS M_BilatAge
,Mother.value(N'(/Relative/OcAge)[1]',N'int') AS M_OcAge
FROM Parents;
But probably it's this you are looking for (any count of <Relative>):
SELECT rel.value(N'(Relation/text())[1]',N'nvarchar(max)') AS Relation
,rel.value(N'(BcAge/text())[1]',N'int') AS BcAge
,rel.value(N'(BilatAge/text())[1]',N'int') AS BilatAge
,rel.value(N'(OcAge/text())[1]',N'int') AS OcAge
FROM #xml.nodes(N'/Root/Relatives/Relative') AS A(rel)

Vars to cases & retain variable/value labels Tableau setup - restructure data for Tableau, flip data

I am flipping my survey data so I can use it in Tableau. Here is example data in SPSS (keep in mind that each variable has value & variable labels).
ID age rate1 rate2 rate3 mr_1 mr_2 mr_3 ...
1 35 8 3 2 1 2
2 40 2 2 3 2
3 41 6 3 5 2 3
4 43 3 3 1
Where rate1-3 are 3 rating questions. Mr_1 to mr_3 is a multiple response check all the apply question (What is your ethnicity? 1=White 2=Hispanic, 3=Black)
I flip the data using this:
VARSTOCASES
/MAKE answer FROM age rate1 rate2 rate3 mr_1 mr_2 mr_3
/INDEX=Index1(7)
/KEEP= All
/NULL=KEEP.
Results look like this:
ID Index1 answer
1 1 35
1 2 8
1 3 3
1 4 2
1 5 1
...
...
...
Which works just fine when connecting this to Tableau. However, what I want is more than just Index1 as an identifier to each variable that has been flipped. What I want is this (Var, VarLab, ValueLabel are just String variables):
ID Var VarLab answer ValueLabel
1 'age' 'What is your age?' 35 '35'
1 'rate1' 'Rate food' 8 '8'
1 'rate2' 'Rate wait time' 3 '3'
1 'rate3' 'Rate bathroom' 2 '2'
1 'mr_1' 'Ethnicity' 1 'White'
1 'mr_2' 'Ethnicity' 2 'Hispanic'
...
...
...
As you can see, I retained the variable label, value label, and the variable name itself for each flipped variable. This is the ideal Tableau setup as Tableau requires "tall" datasets. Also, I can use either the string or numeric representation of the response. Lastly, I no longer need to edit aliases inside of Tableau. Any ideas how to accomplish this? Perhaps this will require python or macro? Any ideas are greatly appreciated.
Thanks!
you need to use OMS to read the dictionary into two datasets - one for variable labels and one for value labels.
then you can match your restructured dataset to the variable labels by variable name, and then match it to the value labels by variable name and value.
Run this to get the two datasets - BEFORE you restructure, of course:
DATASET DECLARE varlab.
OMS /SELECT TABLES /IF COMMANDS=['File Information'] SUBTYPES=['Variable Information']
/DESTINATION FORMAT=SAV OUTFILE='varlab' VIEWER=YES.
DATASET DECLARE vallab.
OMS /SELECT TABLES /IF COMMANDS=['File Information'] SUBTYPES=['Variable Values']
/DESTINATION FORMAT=SAV OUTFILE='vallab' VIEWER=YES.
display dictionary.
omsend.
now restructure and match files - (after renaming the proper variables for matching in the two new datasets).
This is the solution based on the other answer using OMS, and I added few other things.
This flips the vars you want and converts any other var you want to string.
dataset close all.
new file.
get file 'C:\Users\nicholas\Desktop\testFile.sav'.
************************************************************************************************
TABLEAU SETUP
**********************************************
insert file="C:/Users/nicholas/Desktop/Type2syntax.sps".
!toString vars = visitorType.
!flipAndMatch vars = rate1 rate2 rate3 mr_1 mr_2 mr_3.
exe.
*CATEGORIZE FLIPPED VARS
String filter (a150).
!groupingBy 'Rating satis' rate1 rate2 rate3.
!groupingBy 'MR with' mr_1 mr_2 mr_3.
save outfile 'C:\Users\nicholas\Desktop\OtherTableauTest2.sav'.
"C:/Users/nicholas/Desktop/Type2syntax.sps" is :
* Encoding: UTF-8.
save outfile 'C:\Users\nicholas\Desktop\tempSav.sav'.
DATASET DECLARE varlab.
OMS /SELECT TABLES /IF COMMANDS=['File Information'] SUBTYPES=['Variable Information']
/DESTINATION FORMAT=SAV OUTFILE='varlab' VIEWER=YES.
DATASET DECLARE vallab.
OMS /SELECT TABLES /IF COMMANDS=['File Information'] SUBTYPES=['Variable Values']
/DESTINATION FORMAT=SAV OUTFILE='vallab' VIEWER=YES.
display dictionary.
omsend.
DATASET ACTIVATE varlab.
rename variables var1= varName / label = Question.
alter type varName (a20).
alter type Question (a1000).
sort cases by varName.
SAVE OUTFILE='C:\Users\nicholas\Desktop\varlabsTemp.sav'
/keep varName Question.
DATASET ACTIVATE vallab.
rename variables var1=varName / var2 = AnswerNumb / Label = AnswerText.
alter type varName (a20).
alter type AnswerText (a120).
sort cases by varName AnswerNumb.
SAVE OUTFILE='C:\Users\nicholas\Desktop\vallabsTemp.sav'
/keep varName AnswerNumb AnswerText.
dataset close all.
new file.
get file 'C:\Users\nicholas\Desktop\tempSav.sav'.
compute UNIQUE_ID = $casenum.
DEFINE !toString (vars=!CMDEND)
!DO !var !IN (!vars)
!LET !varDelete=!CONCAT("Delete", !var)
rename variables !var = !varDelete.
String !var (a120).
compute !var = valuelabels(!varDelete).
exe.
delete variables !varDelete.
!DOEND
!ENDDEFINE.
DEFINE !groupingBy (!POSITIONAL !TOKENS(1)
/!POSITIONAL !CMDEND)
!DO !var !IN (!2)
!LET !varString=!CONCAT("'", !var,"'")
if varName eq !varString filter eq !1.
!DOEND
exe.
!ENDDEFINE.
DEFINE !flipAndMatch (vars=!CMDEND)
VARSTOCASES
/MAKE AnswerNumb FROM !vars
/INDEX=VarName (AnswerNumb)
/KEEP=ALL
/NULL=KEEP.
EXECUTE.
sort cases by varName AnswerNumb.
alter type varName (a20).
match files files*
/table='C:\Users\nicholas\Desktop\vallabsTemp.sav'
/by varName AnswerNumb.
match files files*
/table='C:\Users\nicholas\Desktop\varlabsTemp.sav'
/by varName.
if AnswerText eq '' AnswerText = string(AnswerNumb, f).
!ENDDEFINE.
Output looks something like this. I didn't flip age or visitorType, but I certainly could have.
UNIQUE_ID VarName AnswerNumb AnswerText Question filter age VisitorType
1 'rate1' 8 '8' 'Rate food' 'Rating group' 35 'Overnight Visitor'
1 'rate2' 3 '3' 'Rate wait time''Rating group' 35 'Overnight Visitor'
1 'rate3' 2 '2' 'Rate bathroom' 'Rating group' 35 'Overnight Visitor'
1 'mr_1' 1 'White' 'Ethnicity' 'MR group' 35 'Overnight Visitor'
...

Adding together similar columns into one column SQL

I'm trying to show the counts of a table of records based on a trackid.
A few of the entries in my ToTransaction column are very similar: Toshi-A,Toshi-B,Toshi-C, Tosan, Toki, Toto
What I want to do in my query is to show all the Toshi's in one row, while still giving Tosan, Toki, and Toto their own rows.
Route ToTransaction Count
F43 Toshi 100
F43 Tosan 200
F43 Toki 75
F43 Toto 125
Instead of
Route ToTransaction Count
F43 Toshi-A 35
F43 Toshi-B 25
F43 Toshi-C 22
F43 Toshi-D 18
F43 Tosan 200
F43 Toki 75
F43 Toto 125
SELECT Route, ToTransaction, count(TrackID) as 'Count' from TestDB
group by Route, ToTransaction
Try this one:
SELECT IF(SUBSTR(ToTransaction,1,5)="Toshi","Toshi",ToTransaction) as "Trans",
COUNT(TracID) as "Count" from TestDB
GROUP BY Trans;
If you use always "-" as seperator and the character count before the "-" character is dynamic you can use substring_index :
For MySQL :
SELECT substring_index(ToTransaction,'-',1) as "Trans",
COUNT(TracID) as "Count" from TestDB
GROUP BY 1;
For MSSQL (I Couldn't try but It should work) :
SELECT CASE WHEN CHARINDEX('-',ToTransaction) > 1
THEN LEFT(ToTransaction,CHARINDEX('-',ToTransaction)-1)
ELSE ToTransaction
END as "Trans",
COUNT(TracID) as "Count" from TestDB GROUP BY Trans;

Extract data from XML string in SQL using XQuery

I am trying to extract data from a XML string which is stored in my table in XMLString column as below..
VId Uid Name TWd XMLString
26 jti jbreti testell string in xml format
26 Man Lomond Mcan string in xml format
26 mw mlwTest tewWell string in xml format
26 tot teapot te2Well string in xml format
XML string having below format. Its having multi level nodes also..
<well uid="b4e952f9">
<name>Demo</name>
<field>Fi Tk</field>
<country>India</country>
<county>South India</county>
<region>SiD</region>
<block>09-365</block>
<timeZone>+09:00</timeZone>
<operator>AACE Oil CO</operator>
<operatorDiv>AAACE South Australia</operatorDiv>
<statusWell>unknown</statusWell>
<wellDatum defaultElevation="true" uid="SL">
<name>Mean Sea Level</name>
<code>SL</code>
</wellDatum>
<waterDepth uom="ft">269.0256</waterDepth>
<wellLocation uid="loc-1">
<latitude uom="dega">-28.601403</latitude>
<longitude uom="dega">137.444458</longitude>
</wellLocation>
<commonData>
<dTimCreation>2012-04-10T13:17:45.959Z</dTimCreation>
<dTimLastChange>2013-11-08T14:42:56.340Z</dTimLastChange>
</commonData>
</well>
I required few nodes from above XML & this few nodes details is also in XML string as below..
<well>
<name></name>
<country></country>
<block></block>
<timeZone></timeZone>
<wellDatum>
<name></name>
<code></code>
</wellDatum>
<waterDepth></waterDepth>
</well>
Now i need to extract nodes from 1st XML with node values string that are present in 2nd XML string.
And out put should be also a XML string.
Output string should be as below....
<well uid="b4e952f9">
<name>Demo</name>
<country>India</country>
<block>09-365</block>
<timeZone>+09:00</timeZone>
<wellDatum defaultElevation="true" uid="SL">
<name>Mean Sea Level</name>
<code>SL</code>
</wellDatum>
<waterDepth uom="ft">269.0256</waterDepth>
</well>
This all i want to be done in MSSQL only.
Can anyone help me..
SELECT XMLString.query(
'<well>
<name>{/well/name/node()}</name>
<country>{/well/country/node()}</country>
<block>{/well/block/node()}</block>
<timeZone>{/well/timeZone/node()}</timeZone>
<wellDatum>
<name>{/well/wellDatum/name/node()}</name>
<code>{/well/wellDatum/code/node()}</code>
</wellDatum>
<waterDepth>{/well/waterDepth/node()}</waterDepth>
</well>'
),
XMLString FROM Well

Cannot produce correct XML format using FOR XML PATH in SQL Server Stored Procedure

I am new to XML and I'm struggling to understand how to write a SQL Server stored procedure to produce a XML file from the following table.
Contract,ContractSection,ClaimReference,TransactionReference,ClaimReference,Ccy,PTT_Ind,PTT_Fees**
P000013-140, P000013-140_001, C000011-14, CLP00000036, C000011-14, GBP, 50, 100
P000066-888, P000066-888_001, C000031-28, CLP00000041, C000011-14, GBP, 75, 200
I have tried, unsuccessfully, to use FOR XML PATH to produce the required format, shown here:
<ReportingContractEntry>
<Contract>
<ReinsRef>P000013-140 </ReinsRef>
<Contract>
<ReportingSectionEntry>
<SectionRef>P000013-140_001 </ SectionRef >
</ReportingSectionEntry>
<ReportingTransactionEntry>
<ReportingTransactionAmountEntry>
<EntryReference>CLP00000036</EntryReference>
<TechAccountAmtItem Type="ptt_ind">
<Amt Share="reinsurer_share" Ccy="GBP">20.00</Amt>
</TechAccountAmtItem>
<TechAccountAmtItem Type="ptt_fees">
<Amt Share="reinsurer_share" Ccy="GBP">590.00</Amt>
</TechAccountAmtItem>
</ReportingTransactionAmountEntry>
</ReportingTransactionEntry>
</ReportingContractEntry>
<ReportingContractEntry>
<Contract>
<ReinsRef>P000066-888 </ReinsRef>
<Contract>
<ReportingSectionEntry>
<SectionRef>P000013-140_001 </ SectionRef >
</ReportingSectionEntry>
<ReportingTransactionEntry>
<ReportingTransactionAmountEntry>
<EntryReference>CLP00000041</EntryReference>
<TechAccountAmtItem Type=" ptt_ind ">
<Amt Share="reinsurer_share" Ccy="GBP">75.00</Amt>
</TechAccountAmtItem>
<TechAccountAmtItem Type="ptt_fees">
<Amt Share="reinsurer_share" Ccy="GBP">200.00</Amt>
</TechAccountAmtItem>
</ReportingTransactionAmountEntry>
</ReportingTransactionEntry>
</ReportingContractEntry>
My best efforts so far is this, but this only works with a single TechAccountAmtItem per ReportingTransactionEntry. And I need to add multiple TechAccountAmtItems
SELECT
RTRIM(policyRef) AS 'Contract/ReinsRef',
RTRIM(policyRef) + '_001' AS 'ReportingSectionEntry/SectionRef',
RTRIM(TransactionReference) AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/EntryReference' ,
'ptt_ind' AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/#Type',
'reinsurer_share' AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt/#Share' ,
CASE WHEN currency_id = 26 THEN 'GBP' ELSE 'EUR' END AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt/#Ccy' ,
SUM(Paid_This_Time_Indemnity) AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt'
--,SUM(Paid_This_Time_Fees) -- I can't figure out how to get a second <TechAmountItem> within the same <ReportingTransactionAmountEntry> ???????
FROM
#tmpClm2
GROUP BY
policyRef, ClaimReference, TransactionReference, ClaimReference, currency_id
FOR XML PATH ('ReportingContractEntry')
As I said, XML is completely new to me so any advice would be greatly appreciated.
I'm not even sure if using FOR XML PATH is the right way to go about this.
It did appear to be the easiest and most flexible option available.
The schema in your example doesn't correspond with your query, but the problem is pretty straightforward. In your query, you can use a subquery where you want multiple elements returned.
Instead of:
'ptt_ind' AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/#Type',
'reinsurer_share' AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt/#Share' ,
CASE WHEN currency_id = 26 THEN 'GBP' ELSE 'EUR' END AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt/#Ccy' ,
SUM(Paid_This_Time_Indemnity) AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry/TechAccountAmtItem/Amt'
Try:
(
SELECT * FROM
(
SELECT
'ptt_ind' AS 'TechAccountAmtItem/#Type',
'reinsurer_share' AS 'TechAccountAmtItem/Amt/#Share' ,
CASE WHEN currency_id = 26 THEN 'GBP' ELSE 'EUR' END AS 'TechAccountAmtItem/Amt/#Ccy' ,
SUM(Paid_This_Time_Indemnity) AS 'TechAccountAmtItem/Amt'
UNION
SELECT
'ptt_fees' AS 'TechAccountAmtItem/#Type',
'reinsurer_share' AS 'TechAccountAmtItem/Amt/#Share' ,
CASE WHEN currency_id = 26 THEN 'GBP' ELSE 'EUR' END AS 'TechAccountAmtItem/Amt/#Ccy' ,
SUM(Paid_This_Time_Fees) AS 'TechAccountAmtItem/Amt'
) iq FOR XML PATH(''), TYPE
) AS 'ReportingTransactionEntry/ReportingTransactionAmountEntry'

Resources