SQL 从 XML 中获取列的逗号分隔值
Posted
技术标签:
【中文标题】SQL 从 XML 中获取列的逗号分隔值【英文标题】:SQL get comma separated values of a column from XML 【发布时间】:2017-03-12 14:53:07 【问题描述】:我正在从存储过程调用标量 UDF 以获取列值。 在标量 UDF 中,我有一个 xml,我必须获取特定节点的逗号分隔值。 我正在使用 Cross Apply,但这会导致巨大的性能问题,因为存储过程实际上是用来获取报告的。
有一个表 [Traveler],它有一个字段 ID、BookingID 和 FareDetails。 在 FareDetails 中,我们存储了 xml。
UDF内部的逻辑如下:
ALTER FUNCTION [dbo].[GetBookingInfo] (@BookingID bigint, @InfoID smallint) RETURNS VARCHAR(1024) AS
BEGIN
DECLARE @InfoCSV VARCHAR(1024)
--
-- Fare Basis: InfoID = 1
--
IF @InfoID = 1
BEGIN
SELECT @InfoCSV = (SELECT
(PTSD.PSTDNode.value('(FBC)[1]', 'VARCHAR(1024)') + ',') [text()]
FROM
[Traveler]
CROSS APPLY [FareDetails].nodes('/AirFareInfo/PTSDPFS/PTSD') PTSD(PSTDNode)
WHERE
[BookingID] = @BookingID
ORDER BY
ID ASC
FOR XML PATH (''))
IF @InfoCSV IS NOT NULL AND LEN(@InfoCSV) > 0
SET @InfoCSV = LEFT(@InfoCSV, LEN(@InfoCSV) - 1)
END
RETURN @InfoCSV
xml如下:
<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false">
<PT>Flight</PT>
<FPMID>0</FPMID>
<PTID>1</PTID>
<FS>
<CID>2</CID>
<Value>0</Value>
</FS>
<TF>
<CID xsi:nil="true" />
<Value>0</Value>
</TF>
<VF>
<CID>2</CID>
<Value>0</Value>
</VF>
<VD>
<CID>2</CID>
<Value>0</Value>
</VD>
<VCR xsi:nil="true" />
<VC>
<CID>2</CID>
<Value>0</Value>
</VC>
<VFC>
<CID>2</CID>
<Value>0</Value>
</VFC>
<VST />
<VIT />
<AAPFVDR xsi:nil="true" />
<CC>
<CID>2</CID>
<Value>0</Value>
</CC>
<D>
<CID>2</CID>
<Value>514.15</Value>
</D>
<PD>
<CID>2</CID>
<Value>0</Value>
</PD>
<EBF>
<CID>2</CID>
<Value>0</Value>
</EBF>
<CST>
<DL>
<ATRID>13</ATRID>
<OB>
<CID>2</CID>
<Value>74.04</Value>
</OB>
<OC>
<CID>2</CID>
<Value>0.00</Value>
</OC>
<OS>
<CID>2</CID>
<Value>0.00</Value>
</OS>
<OF>
<CID>2</CID>
<Value>50.83</Value>
</OF>
<OP>
<CID>2</CID>
<Value>0.00</Value>
</OP>
<C>
<CID>2</CID>
<Value>0</Value>
</C>
<IBF>false</IBF>
<D>2014-06-09T14:57:53.521Z</D>
</DL>
</CST>
<CIT />
<CRMR xsi:nil="true" />
<CRM>
<CID>2</CID>
<Value>0</Value>
</CRM>
<TL ATC="Tax" PC="" DEN="User Development Fee - Arrival (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>75.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Passenger Service Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>146.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Departure (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>1681.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Cute Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>50.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Government Service Tax">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>151.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Arrival (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>833.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Passenger Service Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>1132.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Departure (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>76.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Government Service Tax">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>148.00</Value>
</Amount>
</TL>
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
<ACD RBD="" ACCID="1" MCT="Super Sale Fare(AP)" INC="false" />
<ATSID xsi:nil="true" />
</PTSD>
</PTSDPFS>
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
<ACD RBD="" ACCID="1" MCT="Super Sale Fare(AP)" INC="false" />
<ATSID xsi:nil="true" />
</PTSD>
</PTSDPFS>
<RuleDetails>
<TRS xsi:nil="true" />
<PP xsi:nil="true" />
<II xsi:nil="true" />
<LTD xsi:nil="true" />
</RuleDetails>
</AirFareInfo>
输出应该是AP,AP
请提出更好的替代方案,例如调用 SP,它具有 10000 条记录的此功能需要 15 秒。
我们将不胜感激。
【问题讨论】:
有almost the same question here???无论您(或其他人)可能需要什么:请阅读How to ask a good SQL question 和How to create a MCVE Now there is one more question 内容几乎一样?? 【参考方案1】:而不是使用标量函数将此函数转换为内联表值函数并使用交叉应用类似.....
内联表值函数
CREATE FUNCTION [dbo].[GetBookingInfo_NEW] (@BookingID bigint)
RETURNS TABLE
AS
RETURN (
SELECT t2.BookingID
, (SELECT (PTSD.PSTDNode.value('(FBC)[1]', 'VARCHAR(1024)') + ',') [text()]
FROM [Traveler] t1
CROSS APPLY [FareDetails].nodes('/AirFareInfo/PTSDPFS/PTSD') PTSD(PSTDNode)
WHERE t2.BookingID = t1.BookingID
FOR XML PATH ('')
) FareDetails
FROM [Traveler] t2
WHERE t2.BookingID = @BookingID
)
查询
Select t.ID
, t.BookingID
, REVERSE(STUFF(REVERSE(c.[FareDetails]), 1, 1, ''))
FROM [Traveler] t
CROSS APPLY [dbo].[GetBookingInfo_NEW](t.BookingID) c
【讨论】:
朋友,该函数内没有 switch/case 语句(5),执行不同的逻辑,我编写的代码是这样一个 switch case 语句的一部分。 @RavishKumar 不管逻辑如何,避免使用标量函数,它们会为外部查询返回的每一行执行,因此性能缓慢,使用内联表值函数将逐行转换为基于集合的操作。如果您有 4 种不同的情况,请使用动态 sql 并使用 5 种不同的内联表值函数。 M.Ali:我修改了查询:SELECT @InfoCSV = (SELECT TOP 1 REPLACE(FareDetails.query(N'data(/AirFareInfo/PTSDPFS/PTSD/FBC)').value( '(text())[1]','nvarchar(100)'),' ',',') FROM [Traveler] WHERE [BookingID] = @BookingID) 我节省了 8 秒 将+','
放在开头应该更容易避免双重REVERSE
。 There's a second (almost identical) question,我使用XQuery
的data()
函数放置了一个答案,不需要在nodes()
上使用昂贵的APPLY
... 可能对你来说很有趣...以上是关于SQL 从 XML 中获取列的逗号分隔值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Azure Databricks SQL 中将字段值转换为逗号分隔