Generate xml from ms sql with head and item tables - sql-server

I'd like to generate an xml from MS SQL with a structure like this (invoice head info, then invoice items info):
<?xml version="1.0" encoding="ISO8859-2"?>
<Data HD="1" View="InvoiceGen">
<Row Table="InvoiceHead">
<InvoiceNumber>630506</InvoiceNumber>
<CustomerId>1432</CustomerId>
</Row>
<Row Table="InvoiceItem">
<ItemNumber>B52</ItemNumber>
<Price>320</Price>
<Tax>30</Tax>
</Row>
<Row>
<ItemNumber>B53</ItemNumber>
<Price>330</Price>
<Tax>32</Tax>
</Row>
<Row Table="InvoiceHead">
<InvoiceNumber>630626</InvoiceNumber>
<CustomerId>1556</CustomerId>
</Row>
<Row Table="InvoiceItem">
<ItemNumber>B5</ItemNumber>
<Price>500</Price>
<Tax>55</Tax>
</Row>
<Row>
<ItemNumber>B5</ItemNumber>
<Price>200</Price>
<Tax>20</Tax>
</Row>
<Row>
<ItemNumber>B18</ItemNumber>
<Price>180</Price>
<Tax>16</Tax>
</Row>
</Data>
i have an invoice head table, and an invoice item table (InvoiceNumer makes the connection between the two):
InvoiceHead (InvoiceNumber,CustomerId)
InvoiceItem (InvoiceNumber,ItemNumber,Price,Tax)
I have already created a table with the combined data, with the same structure as in the desired xml:
InvoiceGen(InvoiceNumber,CustomerId,ItemNumber,Price,Tax)
In this table, after a head row there are all the rows with the item information connected to the invoice head. (Just like in the xml)
The contet of this InvoiceGen table is:
InvoiceNumber CustomerId ItemNumber Price Tax
630506 1432 null null null
630506 1432 B52 320 30
630506 1432 B53 330 32
630626 1556 null null null
630626 1556 B5 500 55
630626 1556 B6 200 20
630626 1556 B18 180 16
I'm not sure if this table can help me, but it looked like a good idea.
Can anyone help me to create an xml like the above?
Thank you!

This basically gets you to what you want. Note, however, that 'B18' is sorted before 'B5' and 'B6' because your values are varchars. With a varchar the value '1' and a lower value than '5', and so '18' as a "lower" value than '5':
WITH VTE AS(
SELECT *
FROM (VALUES (630506,1432,NULL,NULL,NULL),
(630506,1432,'B52',320,30),
(630506,1432,'B53',330,32),
(630626,1556,NULL,NULL,NULL),
(630626,1556,'B5',500 ,55),
(630626,1556,'B6',200,20),
(630626,1556,'B18',180,16)) V(InvoiceNumber,CustomerID,ItemNumber,Price,Tax))
SELECT 1 aS [#HD],
'InvoiceGen' AS [#View],
(SELECT CASE WHEN ItemNumber IS NULL THEN 'InvoiceHead' ELSE 'InvoiceItem' END AS [#Table],
CASE WHEN ItemNumber IS NULL THEN InvoiceNumber END AS InvoiceNumber,
CASE WHEN ItemNumber IS NULL THEN CustomerID END AS CustomerID,
ItemNumber,
Price,
Tax
FROM VTE V
ORDER BY V.InvoiceNumber,
V.CustomerID,
CASE WHEN V.ItemNumber IS NULL THEN 0 ELSE 1 END,
V.ItemNumber
FOR XML PATH('Row'),TYPE)
FOR XML PATH('Data');

Related

Stored procedure Read xml file with namespaces

I am trying to read the following xml using a stored procedure in SQL server 2016.
DECLARE #xml as xml
SET #xml = '<ns:MT_FlexParams_SPICE xmlns:ns="urn:ptl.com:pi:imdl">
<row>
<ACCT>100043</ACCT>
<BROL>10085437</BROLE>
<STRTDAT>2019-11-01 00:00:00.0</STRTDAT>
<ENDDAT>2021-05-15 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE> </DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10126</IRQID>
<BRAUT_GRA> </BRAUT_GRA>
<BRAUT_REV> </BRAUT_REV>
</row>
<row>
<ACCT>100037</ACCT>
<BROL>10085437</BROLE>
<STRTDAT>2019-10-21 00:00:00.0</STRTDAT>
<ENDDAT>2020-10-21 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE> </DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10106</IRQID>
<BRAUT_GRA> </BRAUT_GRA>
<BRAUT_REV> </BRAUT_REV>
</row>
</ns:MT_FlexParams_SPICE>'
WITH XMLNAMESPACES (DEFAULT 'urn:ptl.com:pi:imdl')
SELECT #xml.value('(row/ACCT)[1]', 'nvarchar(100)') as ACCT;
But it is fetching null result. Could you please let me know the mistake that I am performing?
The XML is not well-formed, so I had to fix it.
You need to use .nodes() method to shred the XML into a rectangular structure.
SQL
DECLARE #xml AS XML = N'<ns:MT_FlexParams_SPICE xmlns:ns="urn:ptl.com:pi:imdl">
<row>
<ACCT>100043</ACCT>
<BROL>10085437</BROL>
<STRTDAT>2019-11-01 00:00:00.0</STRTDAT>
<ENDDAT>2021-05-15 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE>
</DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10126</IRQID>
<BRAUT_GRA>
</BRAUT_GRA>
<BRAUT_REV>
</BRAUT_REV>
</row>
<row>
<ACCT>100037</ACCT>
<BROL>10085437</BROL>
<STRTDAT>2019-10-21 00:00:00.0</STRTDAT>
<ENDDAT>2020-10-21 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE>
</DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10106</IRQID>
<BRAUT_GRA>
</BRAUT_GRA>
<BRAUT_REV>
</BRAUT_REV>
</row>
</ns:MT_FlexParams_SPICE>';
;WITH XMLNAMESPACES ('urn:ptl.com:pi:imdl' AS ns)
SELECT c.value('(ACCT/text())[1]', 'VARCHAR(100)') as ACCT
, c.value('(BROL/text())[1]', 'VARCHAR(100)') as BROL
FROM #xml.nodes('/ns:MT_FlexParams_SPICE/row') AS t(c);
Output
+--------+----------+
| ACCT | BROL |
+--------+----------+
| 100043 | 10085437 |
| 100037 | 10085437 |
+--------+----------+

Update column from another table based on matching column value using fuzzy logic

How to update a column from another table on the basis of percentage of matching of two another column of two tables
e.g Suppose i have table Company1 having column Address1 and CompanyName1 and another table Company2 having column Address2 and CompanyName2 .I want to update CompanyName1 of Company1 table from Company2 on the basis of Address1 and Address2
matching percent value.
Please suggest me a efficient running query
Based on your comment:
;WITH Company2 as (
SELECT 'Hamby Chiropractic' as CompanyName2,
'6716 Madison Ave # A1' as Address2
), Company1 as (
SELECT NULL as CompanyName1,
'6716 Madison' as Address1
)
SELECT *, LEN(c1.Address1)*100.00/LEN(c2.Address2)
FROM Company1 c1
LEFT JOIN Company2 c2
ON c2.Address2 LIKE '%' + c1.Address1 +'%'
OR c1.Address1 LIKE '%' + c2.Address2 +'%'
I will get
CompanyName1 Address1 CompanyName2 Address2 (No column name)
NULL 6716 Madison Hamby Chiropractic 6716 Madison Ave # A1 57.1428571428571
I have counted the percent based on the length of strings. Is that sufficient enough for you?

Remove and Track Items Bought and Returned

I have a table which records all the transaction details for items. The format looks like the following columns:
Item
Transaction Type (Sale, Purchase, Vendor Return,Sales Return)
Change Description (detail along with OrderNumber)
Quantity Added
Quantity Subtracted
I am trying to get the Last Purchase Date, and the Last Sale Date which was not returned. For example a purchase was made on OrderNumber 1256 on May 15, 2015 for 144 Items and the next day there is a Transaction for a return on OrderNumber 1256. I'm not sure how I can approach this. Would I create another column which has Returned (True or False) and then update that depending on if there was a return associated to this?
Would this be achieved via stored procedure or a simple query?
Here is the sample data:
Item TransactionDate TransactionType ChangeDescription AdditionQty SubtractionQty
1006 2015-05-27 VENDOR RETURN RETURN NO. 423 0 -144
1006 2015-05-28 PURCHASE PURCH NO. 423 144 0
So When I am pulling up the Last Purchase Date, this would essentially be removed because this was purchased but returned.
If I correctly understood and you have an order number in item, this could be what you are looking for; of course with more info it could be easier ...
SELECT Item, LastPurchaseDate
FROM (
SELECT Item, MAX(TransactionDate) LastPurchaseDate, SUM(AdditionQty) AdditionQty, SUM(SubtractionQty) SubtractionQty
FROM Table
GROUP BY Item ) A
WHERE (A.AdditionQty + A.SubtractionQty) != 0
Using a temp table:
select Item, TransactionDate, LEFT(ChangeDescription,3) as TransType, RIGHT(a.ChangeDescription,LEN(a.ChangeDescription)-(CHARINDEX('.',a.ChangeDescription) + 1) as OrderNo
into #temp
from [table]
SELECT a.Item,MAX(a.TransactionDate),a.TransType,a.OrderNo
FROM #temp a
INNER JOIN #temp b
ON a.OrderNo = b.OrderNo AND a.TransType <> b.TransType
GROUP BY a.TransType

Parse XML column in SQL Server

I have a XML column in SQL Server. Example data below
<row id="AC.1.TR.AUD.12800............" xml:space="preserve">
<c1>AC</c1>
<c2>1</c2>
<c3>TR</c3>
<c4>AUD</c4>
<c5>12800</c5>
<c17>20150129</c17>
<c18>CREDIT</c18>
<c18 m="2">DEBIT</c18>
<c19>4289540.22</c19>
<c19 m="2">-17955</c19>
<c20 m="2" />
<c21 m="2" />
<c22>52287350.51</c22>
<c22 m="2">-218862.47</c22>
<c23>-688471.2</c23>
<c23 m="2" />
<c24 m="2">2881.77</c24>
<c32 />
</row>
Starting from column c18 to c24 all are associated. If there are two sets in 18, then there will two group set. However,none of tags inside the two sets are mandatory. I need to parse these into a normal table structure.
Here is the proper output:
RECID C18 C19 C22 C23 C24
AC.1.TR.AUD.12800......... CREDIT 428950.22 52287350.51 -688471.2
AC.1.TR.AUD.12800......... DEBIT -17955.00 -218862.47 2881.77
Note: I have tried nodes and values options but none are helping me out in getting the related values between the tags.
You could use a PIVOT to get the row data into columns:
SELECT ID, C18,C19,C22,C23,C24
FROM
(
SELECT
Loc.value('../#id', 'varchar(255)') ID,
isnull(Loc.value('#m', 'varchar(100)'),'') m,
Loc.value('local-name(.)[1]', 'varchar(100)') c,
Loc.value('.', 'varchar(100)') cValue
FROM #XML.nodes('/row/child::node()') as T(Loc)
) AS SourceTable
PIVOT
(
Max(cValue)
FOR c IN (C18,C19,C22,C23,C24)
) AS PivotTable
order by 1, 2

Order by XML attribute value

I have the following table
EntityID | Data
-----------+-----------
1 | <xml data>
2 | <xml data>
3 | <xml data>
Where XML DATA looks like this:
<Qualifications>
<Qualification QualificationName="Has Dr's degree" Remark="Yes" />
<Qualification QualificationName="ASP.NET Experience" Remark="1 Year" />
<Qualification QualificationName="Sex" Remark="M" />
</Qualifications>
I'd like to have the ability to order by remark for a particular QualificationName
SELECT * FROM Table
....
ORDER BY 'ASP.NET Experience'
P.S.
Potencially I can change XML to something like this to make things simplier
<Qualifications>
<Has Dr's degree>Yes</Has Dr's degree>
<ASP.NET Experience>1 Year</ASP.NET Experience>
<Sex>M</Sex>
</Qualifications>
UPD1: For case when user wants to order by 'ASP.NET Experience' qualification The expected result would be like this:
EntityID | Data
-----------+-----------
3 | <xml data>
1 | <xml data>
2 | <xml data>
Because EntityID 3 has Remark '1 year' EntityID 1 has remark '2 years' and EntityID 2 has remark '3 years' inside XML column for 'ASP.NET Experience' qualification
Assuming #QualificationName identifies the particular node you want to order by this will give you the value of the Remark.
declare #xml xml; set #xml = '<Qualifications>
<Qualification QualificationName="Has Dr%quot;s degree" Remark="Yes" />
<Qualification QualificationName="ASP.NET Experience" Remark="1 Year" />
<Qualification QualificationName="Sex" Remark="M" />
</Qualifications>'
declare #Qualification nvarchar(100); set #Qualification = 'ASP.NET Experience'
select #xml.value('(//Qualifications/Qualification[#QualificationName=sql:variable("#Qualification")]/#Remark)[1]', 'varchar(10)')
I assumed your xml as:
<Qualifications>
<HasDrsdegree>Yes</HasDrsdegree>
<ASPNETExperience>2</ASPNETExperience>
<Sex>M</Sex>
</Qualifications>
I hope this query will resolve your problem, this query results one extra column which you can ignore but this will solve you problem for complete table in one go.
Select EntityID,Data,T2.Loc.value('.','int') as 'experience'
from [yourtablename]
cross apply Data.nodes('/Qualifications/ASPNETExperience') as T2(Loc)
order by experience

Resources