Include Blank Tag in XML - sql-server

Using the below SQL query I am generating an XML File like below.
SELECT CAST((SELECT
(
SELECT
ICase,
DPCRef
FROM
#HD HD WHERE HD.OrdRef=PICK.OrdRef
GROUP BY
ICase,
DPCRef
FOR XML PATH('HD'), TYPE
),
(
SELECT
PCode,
SOLnNt,
(
SELECT SSCCRef
FROM #SSCC LM
WHERE LD.OrdRef = LM.OrdRef AND LD.PCode=LM.PCode
GROUP BY
SSCCRef
FOR XML PATH(''), ROOT('SSCC'), TYPE
)
FROM #LD LD
WHERE LD.OrdRef = PICK.OrdRef
GROUP BY
PCode,
SOLnNt
FOR XML PATH('LD'), TYPE
)
FROM #PICK PICK
GROUP BY OrdRef
FOR XML PATH('PICK'), ROOT('PickOrder')) AS XML) AS OrdersPickingConfirmationXML
Result:
<PICK>
<HD>
<CustCode>PAV001</CustCode>
<OrdRef>813804</OrdRef>
</HD>
<LD>
<PCode>PROD0001</PCode>
<Rotation>14/D004193</Rotation>
<SSCC>
<SSCCRef>TEST 1</SSCCRef>
<SSCCRef>TEST1</SSCCRef>
</SSCC>
</LD>
</PICK>
Sometime there will be no data in the #SSCC table.So that time that SSCC tag is not showing in XML.If there is no data I need to show it as </SSCC>. Is there anyway that I can get blank tag </SSCC>

Related

How do I query repeating XML child nodes inside parent repeating nodes?

I have the following XML stored in an nText column (not my design, older database). I need to pull the PolicyNumber and the CvgCode that is a property of Coverage child node.
<efs:Request
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:efs="http://www.slsot.org/efs"
xsi:schemaLocation="http://www.slsot.org/efs
http://efs.slsot.org/efs/xsd/SlsotEfsSchema2.xsd">
<EfsVersion>2.0</EfsVersion>
<Batch BatchType="N" AgLicNo="12345" ItemCnt="69">
<EFSPolicy>
<PolicyNumber>POL12345</PolicyNumber>
<Binder>0086592YZ</Binder>
<TransType>N</TransType>
<Insured>Dummy Co LLC</Insured>
<ZipCode>75225</ZipCode>
<ClassCd>99930</ClassCd>
<PolicyFee>35.00</PolicyFee>
<TotalTax>36.62</TotalTax>
<TotalStampFee>1.13</TotalStampFee>
<TotalGross>792.75</TotalGross>
<EffectiveDate>09/17/2018</EffectiveDate>
<ExpirationDate>09/17/2019</ExpirationDate>
<IssueDate>09/20/2018</IssueDate>
<ContUntilCancl>N</ContUntilCancl>
<FedCrUnion>N</FedCrUnion>
<AORFlag>N</AORFlag>
<CustomID>043684</CustomID>
<WindStormExclusion>N</WindStormExclusion>
<CorrectionReEntry>N</CorrectionReEntry>
<Coverages>
<Coverage CvgCode="9325">720.00</Coverage>
</Coverages>
<Securities>
<Company CoNumber="80101168">100.00</Company>
</Securities>
</EFSPolicy>
<EFSPolicy>
...
</EFSPolicy>
</Batch>
</efs:Request>
And here is the SQL code I am using to extract the PolicyNumber (so far).
with cte_table(BatchID, xmlData)
AS
(SELECT BatchID, CAST(CAST(xmlData AS VARCHAR(MAX)) AS XML) from
Batches)
select
s.BatchID
,t.c.value('PolicyNumber[1]', 'varchar(max)') as PolicyNumber
from cte_table as s
cross apply s.xmlData.nodes('/*:Request/Batch/EFSPolicy') as t(c)
where BatchID in (select batchID from Batches where CreateDate between '1/1/19' and getdate())
I have tried a second CROSS APPLY on the Coverages node, but that was giving me all the Coverage values (not CvgCode property) for every batch, so my result set was 100+ times too many rows. I assume that was due to the 2nd CROSS APPLY, is there a INNER JOIN type CROSS APPLY?
You need to declare your namespaces to retrieve the data:
DECLARE #XML xml = '<efs:Request
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:efs="http://www.slsot.org/efs"
xsi:schemaLocation="http://www.slsot.org/efs
http://efs.slsot.org/efs/xsd/SlsotEfsSchema2.xsd">
<EfsVersion>2.0</EfsVersion>
<Batch BatchType="N" AgLicNo="12345" ItemCnt="69">
<EFSPolicy>
<PolicyNumber>POL12345</PolicyNumber>
<Binder>0086592YZ</Binder>
<TransType>N</TransType>
<Insured>Dummy Co LLC</Insured>
<ZipCode>75225</ZipCode>
<ClassCd>99930</ClassCd>
<PolicyFee>35.00</PolicyFee>
<TotalTax>36.62</TotalTax>
<TotalStampFee>1.13</TotalStampFee>
<TotalGross>792.75</TotalGross>
<EffectiveDate>09/17/2018</EffectiveDate>
<ExpirationDate>09/17/2019</ExpirationDate>
<IssueDate>09/20/2018</IssueDate>
<ContUntilCancl>N</ContUntilCancl>
<FedCrUnion>N</FedCrUnion>
<AORFlag>N</AORFlag>
<CustomID>043684</CustomID>
<WindStormExclusion>N</WindStormExclusion>
<CorrectionReEntry>N</CorrectionReEntry>
<Coverages>
<Coverage CvgCode="9325">720.00</Coverage>
</Coverages>
<Securities>
<Company CoNumber="80101168">100.00</Company>
</Securities>
</EFSPolicy>
<EFSPolicy>
</EFSPolicy>
</Batch>
</efs:Request>';
WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema-instance' as xsi,
'http://www.slsot.org/efs' AS efs)
SELECT EFS.[Policy].value('(./PolicyNumber/text())[1]','varchar(25)') AS PolicyNumber,
EFS.[Policy].value('(./Coverages/Coverage/#CvgCode)[1]','int') AS CvgCode --Assumes only 1 CvgCode per policy
FROM (VALUES(#XML)) V(X)
CROSS APPLY V.X.nodes('efs:Request/Batch/EFSPolicy') EFS([Policy]);

Create View - Declare a variable

I am creating a view that is using that STUFF function. I want to put the result of STUFF in a variable for my view. The problem I am having is declaring my variable. It gives me the message "Incorrect Syntax near 'DECLARE'. Expecting '(' or SELECT." I already have the '(' in there. I have tried putting a BEGIN before it. I have tried putting it after the SELECT word. But nothing seems to work and I cannot find a solution in my search. I am using SQL Server 2012
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
(DECLARE #CONDITIONS AS varchar(20)
SET #CONDITIONS = (SELECT DISTINCT BD.[RequestedBurnsID]
,[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20),[ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions] WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD)
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT
,#CONDITIONS AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
GO
You can't declare variables in a view. Could you make it into a function or stored procedure?
Edit - you might also be able to put something into a CTE (Common Table Expression) and keep it as a view.
e.g.
WITH conditions as
(
... do the STUFF here
)
SELECT blah
FROM blah
INNER JOIN conditions
(or CROSS JOIN conditions if its just one row, I can't quite decipher what your data is like)
Here is a sample query that uses a CTE (Common Table Expression) to nicely emulate internal variable construction, as described by James Casey. You can test-run it in your version of SQL Server.
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
yielding output:
status name
12 dbo
0 db_accessadmin
0 db_securityadmin
0 db_ddladmin
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Or use a CTE (common table expression) as subselect like:
WITH CTE_Time(Clock)
AS(
SELECT 11 AS [Clock] -- set var
)
SELECT
DATEPART(HOUR, GETDATE()) AS 'actual hour',
CASE
WHEN DATEPART(HOUR, GETDATE()) >= (SELECT [Clock] FROM CTE_Time) THEN 'after'
ELSE 'before'
END AS [Data]
Try put the condition subquery directly inside the the view select statement. you may CAST the XML to VARCHAR(20).
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT,
(
SELECT DISTINCT BD.[RequestedBurnsID],
[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20), [ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions]
WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD
) AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID

sql tvp vs xml speed in stored procedure are the same??? Is my testing or logic flawed?

I'm an accidental DBA charged with speeding up all our sql servers. I've got a highly used query with a horrible average worker time. I noticed it uses XML to pass data to a stored procedure. Query plan tells me it spends most of its time converting XML. Everything I've read says XML is about 33% slower than TVP. I rewrote the SP using TVP and compared times using the method:
SELECT #StartTime=GETDATE()
exec GetTVPData3 #tvp --or XML method
SELECT #EndTime=GETDATE()
SELECT DATEDIFF(ms,#StartTime,#EndTime) AS [Duration in millisecs]
After many runs and averaging out the times.... TVP vs XML has XML winning by 5ms. ????? (550ms vs 545ms) Is my testing or logic flawed?
Both XML and TVP are populated before I get StartTime. I've run this on 2 different SQL test servers with similar results.
The particular code is used in a cross apply. The only difference in the SPs are:
**TVP**
CROSS APPLY (SELECT id AS ProductID, sortorder AS SortOrder FROM #Insert_tvp) Items
**XML**
CROSS APPLY (
SELECT f.id.value('#id', 'int') AS ProductID, f.id.value('#sortorder', 'int') AS SortOrder
FROM #ProductIDs.nodes('list/p')
AS f(id)
) Items
Everything in my head tells me we need to switch to using TVP and get rid of XML. But I can't convince coders without better results.
EDIT: Adding the whole XML SP:
ALTER PROCEDURE [dbo].[ExtendedDataXML]
#HostedSiteID INT,
#ProductIDs XML = NULL,
#ImageType VARCHAR(20) = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT
Products.ID AS ItemID,
0 AS ItemType,
Products.SKU,
Products.Title,
HSP.Slug,
Products.Rank,
Products.Rank AS SalesRank,
Products.Status,
Products.LaunchDate,
Products.IsOnline,
Products.IsAutoOffline,
Products.IsSalableOnline,
Products.IsMarketableOnline,
Products.LeadIn, Products.LeadOut,
COALESCE(Products.CaseQuantity, 1) AS CaseQuantity,
COALESCE(Products.MinimumOrderQuantity, 1) AS MinimumOrderQuantity,
Products.QuantityOnHand,
Image.Filename, Image.Width, Image.Height, Image.Alt, Image.Title,
Pricing.Price, Pricing.SalePrice,
Products.TruckShipment,
HSP.NDescription
FROM Products
JOIN HostedSites_Products HSP ON Products.ID = HSP.ProductID
CROSS APPLY (
SELECT f.id.value('#id', 'int') AS ProductID, f.id.value('#sortorder', 'int') AS SortOrder
FROM #ProductIDs.nodes('list/p')
AS f(id)
) Items
OUTER APPLY (
SELECT TOP(1) Filename, Width, Height, Alt, Title
FROM Items_Images
JOIN Images ON Items_Images.ImageID = Images.ID
WHERE Items_Images.ItemID = Products.ID
AND Items_Images.ItemType = 0
AND Images.Type = COALESCE(#ImageType, '.4b')
) Image
OUTER APPLY (
SELECT TOP(1) Price, SalePrice, CurrentPrice
FROM ProductPrices
WHERE ProductPrices.ProductID = Products.ID
ORDER BY LoRange ASC
) Pricing
WHERE Products.ID = Items.ProductID
AND HSP.HostedSiteID = #HostedSiteID
AND HSP.Validated = 1
AND Products.IsMarketableOnline = 1
ORDER BY Items.SortOrder
END
CROSS APPLY means row wise execution! You are parsing your XML over and over...
Your ID-List is - as far as I understand - meant as a filter
Besides the fact, that this was much better done within an inlined TVF (syntax without BEGIN...END you might try this like this:
ALTER PROCEDURE [dbo].[ExtendedDataXML]
#HostedSiteID INT,
#ProductIDs XML = NULL,
#ImageType VARCHAR(20) = NULL
AS
BEGIN
SET NOCOUNT ON;
WITH IDList AS
(
SELECT f.id.value('#id', 'int') AS ProductID, f.id.value('#sortorder', 'int') AS SortOrder
FROM #ProductIDs.nodes('list/p') AS f(id)
)
SELECT
Products.ID AS ItemID,
0 AS ItemType,
Products.SKU,
Products.Title,
HSP.Slug,
Products.Rank,
Products.Rank AS SalesRank,
Products.Status,
Products.LaunchDate,
Products.IsOnline,
Products.IsAutoOffline,
Products.IsSalableOnline,
Products.IsMarketableOnline,
Products.LeadIn, Products.LeadOut,
COALESCE(Products.CaseQuantity, 1) AS CaseQuantity,
COALESCE(Products.MinimumOrderQuantity, 1) AS MinimumOrderQuantity,
Products.QuantityOnHand,
Image.Filename, Image.Width, Image.Height, Image.Alt, Image.Title,
Pricing.Price, Pricing.SalePrice,
Products.TruckShipment,
HSP.NDescription
FROM Products
JOIN HostedSites_Products HSP ON HSP.HostedSiteID = #HostedSiteID AND HSP.Validated = 1 AND Products.ID = HSP.ProductID
INNER JOIN IDList AS Items ON Items.ProductID=Products.ProductID
OUTER APPLY (
SELECT TOP(1) Filename, Width, Height, Alt, Title
FROM Items_Images
JOIN Images ON Items_Images.ImageID = Images.ID
WHERE Items_Images.ItemID = Products.ID
AND Items_Images.ItemType = 0
AND Images.Type = COALESCE(#ImageType, '.4b')
) Image
OUTER APPLY (
SELECT TOP(1) Price, SalePrice, CurrentPrice
FROM ProductPrices
WHERE ProductPrices.ProductID = Products.ID
ORDER BY LoRange ASC
) Pricing
WHERE Products.IsMarketableOnline = 1
ORDER BY Items.SortOrder
END

SQL Server 2014 "for xml path" into View

I have a query that works well on its own, putting multiple values into a single column:
select substring(
(
select '/'+ Convert(nvarchar,OE) AS 'data()'
from wpb1.dbo.OE_table
where QDBID = 605441478
for xml path('')
)
,2, 255) as list1
And I have this View that I want to put it into, which works fine on its own:
SELECT
DISTINCT wpb1.dbo.Quotes.[Quote Num], MIN(wpb1.dbo.OE_table.OE) AS [Min OE],
COUNT(wpb1.dbo.OE_table.OE) AS [Count OE]
FROM wpb1.dbo.Quotes
LEFT OUTER JOIN wpb1.dbo.OE_table
ON wpb1.dbo.Quotes.QuoteDBID = wpb1.dbo.OE_table.QDBID
GROUP BY wpb1.dbo.Quotes.[Quote Num]
But when I try it causes an error saying:
Error in WHERE clause near '('. Error in GROUP BY clause. Unable to
parse query text.
This is what I am trying:
SELECT
DISTINCT wpb1.dbo.Quotes.[Quote Num], MIN(wpb1.dbo.OE_table.OE) AS [Min OE],
COUNT(wpb1.dbo.OE_table.OE) AS [Count OE],
(
select substring(
(
select '/'+ Convert(nvarchar,OE) AS 'data()'
from wpb1.dbo.OE_table
where QDBID = 605441478
for xml path('')
),2, 255)
) as list1
FROM wpb1.dbo.Quotes LEFT OUTER JOIN wpb1.dbo.OE_table
ON wpb1.dbo.Quotes.QuoteDBID = wpb1.dbo.OE_table.QDBID
GROUP BY wpb1.dbo.Quotes.[Quote Num]
What am I doing wrong?
Using SQL Server 2014.

SQL Server 2012: Select xml with repeated and ungrouped set of elements

For the XML below:
<Document>
<ID>01</ID>
<RaitingDate>2006-05-04T18:13:51.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner1</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:52.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner2</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:53.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner3</MinimumRatingPartner>
</Document>
I would like to generate the following table:
RatingDate MRP
----------------------- ---------------------
2006-05-04 18:13:51.000 MinimumRatingPartner1
2006-05-04 18:13:52.000 MinimumRatingPartner2
2006-05-04 18:13:53.000 MinimumRatingPartner3
Now I am getting:
RatingDate MRP
----------------------- ---------------------
2006-05-04 18:13:51.000 MinimumRatingPartner1
2006-05-04 18:13:52.000 MinimumRatingPartner1
2006-05-04 18:13:53.000 MinimumRatingPartner1
2006-05-04 18:13:51.000 MinimumRatingPartner2
2006-05-04 18:13:52.000 MinimumRatingPartner2
2006-05-04 18:13:53.000 MinimumRatingPartner2
2006-05-04 18:13:51.000 MinimumRatingPartner3
2006-05-04 18:13:52.000 MinimumRatingPartner3
2006-05-04 18:13:53.000 MinimumRatingPartner3
Using this query:
DECLARE #XML XML =
'<Document>
<ID>01</ID>
<RaitingDate>2006-05-04T18:13:51.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner1</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:52.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner2</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:53.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner3</MinimumRatingPartner>
</Document>'
SELECT
RatingDate = s.value('text()[1]', 'datetime')
,MRP =r.value('text()[1]', 'nvarchar(50)')
FROM
#XML.nodes('Document') as D(V)
cross apply
D.V.nodes('./RaitingDate') as Q(S)
cross apply
D.V.nodes('./MinimumRatingPartner') as M(R)
order by MRP, RatingDate
I have tried couple other queries, but without success.
Please note: XML structure cannot be changed.
Your XML appears to depend on ordered pairs (first partner goes with first date, second partner goes with second date etc etc). Not only that, but both columns are listed within the same parent node. So you'll have to do something like this. Lucky for you xml is order sensitive.
DECLARE #XML XML =
'<Document>
<ID>01</ID>
<RaitingDate>2006-05-04T18:13:51.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner1</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:52.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner2</MinimumRatingPartner>
<RaitingDate>2006-05-04T18:13:53.0Z</RaitingDate>
<MinimumRatingPartner>MinimumRatingPartner3</MinimumRatingPartner>
</Document>'
SELECT MinimumRatingPartner, RatingDate FROM
(SELECT
D.V.value('text()[1]', 'datetime') AS RatingDate,
ROW_NUMBER() over (order by ##rowcount) AS RowNum
FROM #XML.nodes('Document/RaitingDate') as D(V)) Dates
INNER JOIN
(SELECT
D.V.value('text()[1]', 'nvarchar(50)') AS MinimumRatingPartner,
ROW_NUMBER() over (order by ##rowcount) AS RowNum
FROM #XML.nodes('Document/MinimumRatingPartner') as D(V)) Partners
ON Dates.RowNum = Partners.RowNum
Probably not the best solution, but works for the given xml
SELECT RaitingDate,MinimumRatingPartner
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY RaitingDate) AS Num
,RaitingDate
FROM
(
SELECT
Node.Data.value('(.)[1]','DATETIME') as RaitingDate
FROM #XML.nodes('/Document/RaitingDate') Node(Data)
) AS A
) AS DateTable
JOIN
(
SELECT ROW_NUMBER() OVER(ORDER BY MinimumRatingPartner) AS Num
,MinimumRatingPartner
FROM
(
SELECT
Node.Data.value('(.)[1]','VARCHAR(50)') as MinimumRatingPartner
FROM #XML.nodes('/Document/MinimumRatingPartner') Node(Data)
) AS B
) AS PartnerTable
ON DateTable.Num=PartnerTable.Num

Resources