我正在从存储过程中调用Scalar UDF以获取列值。在标量UDF内,我有一个xml,并且必须获取特定节点的逗号分隔值。我使用了Cross apply,但是它导致了巨大的性能瓶颈,因为存储过程实际上是用来获取报告的。
有一个[旅行者]表,其中包含字段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
第二种解决方案,无交叉适用:
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 TOP 1 REPLACE(FareDetails.query(N'data(/AirFareInfo/PTSDPFS/PTSD/FBC)').value('(text())[1]','nvarchar(100)'),' ',',') FROM [Traveler] WHERE [BookingID] = @BookingID) IF @InfoCSV IS NOT NULL AND LEN(@InfoCSV) > 0 SET @InfoCSV = LEFT(@InfoCSV, LEN(@InfoCSV) - 1) END RETURN @InfoCSV
第二种解决方案节省了很多时间,但是当我们有重复的预订ID时,它并没有将FBC的所有值串联在一起。例如:1)如果BookingID是唯一的,并且我们有如下的FareDetail xml,那么输出应该是AP,AP 2)如果BookingID不是唯一的(来了两次),并且我们有FareDetail xml,那么输出应该是AP,AP, AP,AP对应于两个BookingID。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>
请提出如何牢记性能的建议。
这是一个完整的示例。
您告诉我们,性能很重要,所以 不要使用标量UDF!
像这样尝试(下一次,创建( 减少!!! )MCVE是您的工作:
CREATE DATABASE testDB; GO USE testDB; GO CREATE TABLE Booking(BookingID INT CONSTRAINT PK_Booking PRIMARY KEY ,SomeBookingData VARCHAR(100)); INSERT INTO Booking VALUES(1,'Booking 1'),(2,'Booking 2'); CREATE TABLE BookingInfo(BookingID INT CONSTRAINT FK_BookingInfo_BookingID FOREIGN KEY REFERENCES Booking(BookingID) ,SomeOtherInfo VARCHAR(100) ,FareDetails XML); INSERT INTO BookingInfo VALUES (1,'First row for ID=1, returns AP,AP' ,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false"> <PTSDPFS> <PTSD IO="false"> <FBC>AP</FBC> </PTSD> </PTSDPFS> <PTSDPFS> <PTSD IO="false"> <FBC>AP</FBC> </PTSD> </PTSDPFS> </AirFareInfo>') ,(1,'Second row for ID=1, returns XY,MN' ,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false"> <PTSDPFS> <PTSD IO="false"> <FBC>XY</FBC> </PTSD> </PTSDPFS> <PTSDPFS> <PTSD IO="false"> <FBC>MN</FBC> </PTSD> </PTSDPFS> </AirFareInfo>') ,(2,'row with ID=2, returns AA,BB' ,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false"> <PTSDPFS> <PTSD IO="false"> <FBC>AA</FBC> </PTSD> </PTSDPFS> <PTSDPFS> <PTSD IO="false"> <FBC>BB</FBC> </PTSD> </PTSDPFS> </AirFareInfo>'); GO
-这是功能。它返回as table并完全内联(不BEGIN...END!)
as table
BEGIN...END
CREATE FUNCTION dbo.CreateBookingInfoCSV(@BookingID INT) RETURNS TABLE AS RETURN SELECT STUFF( ( SELECT ','+REPLACE(FareDetails.query(N'data(/AirFareInfo/PTSDPFS/PTSD/FBC)').value(N'.',N'nvarchar(max)'),' ',',') FROM BookingInfo AS bi WHERE bi.BookingID=@BookingID FOR XML PATH('') ),1,1,'') AS BookingInfoCSV; GO
-如果您的值中包含空格,则 提示 技巧XQuery data() function将中断!
XQuery data() function
-以下SELECT从Booking-table调用所有行并获取合适的详细信息
SELECT
Booking
SELECT b.BookingID ,b.SomeBookingData ,A.BookingInfoCSV FROM Booking AS b OUTER APPLY dbo.CreateBookingInfoCSV(b.BookingID) AS A; GO
-清理( 仔细 查看 真实数据! )
USE master; GO DROP DATABASE testDB;
- 结果
BookingID SomeBookingData BookingInfoCSV 1 Booking 1 AP,AP,XY,MN 2 Booking 2 AA,BB