2017-03-14 3 views
0

J'appelle Scalar UDF d'une procédure stockée pour obtenir une valeur de colonne. A l'intérieur de l'UDF scalaire, j'ai un XML et je dois obtenir les valeurs séparées par des virgules d'un nœud particulier. J'ai utilisé Cross apply, mais cela a causé un énorme goulot d'étranglement au niveau des performances car la procédure stockée est en fait utilisée pour récupérer des rapports.Récupère les valeurs séparées par des virgules d'un fichier xml dans SQL

Il existe une table [Traveler] avec un ID de champ, BookingID (peut être dupliqué) et FareDetails. A l'intérieur des FareDetails, nous stockons le fichier XML.

La logique à l'intérieur UDF est la suivante: 1ère solution, Utiliser Cross Appliquer:

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 

2ème solution, sans croisée se:

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 

La 2ème solution est beaucoup gain de temps, mais quand nous avons des identifiants de réservation en double, alors il ne concatène pas toutes les valeurs de FBC. par exemple. : 1) Si BookingID est unique et nous avons FareDetail xml comme suit alors la sortie devrait être AP, AP 2) Si BookingID n'est pas unique (arrivant deux fois) et nous avons FareDetail xml comme suit alors la sortie devrait être AP, AP , AP, AP correspondant aux deux BookingID. Le XML est comme suit:

<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> 

S'il vous plaît indiquer comment il peut être fait en gardant à l'esprit la performance.

+0

Quelle est SGBDR cela? Veuillez ajouter une balise pour spécifier si vous utilisez 'mysql',' postgresql', 'sql-server',' oracle' ou 'db2' - ou autre chose entièrement. –

Répondre

0

Ceci est un exemple complet.

Vous nous avez dit que les performances sont importantes, donc n'utilisez pas les UDF scalaires!

Essayez comme ça (la prochaine fois, il est votre travail à create a (reduced!!!) 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 

--Ce est la fonction Elle retourne as table et est entièrement inline (pas 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 [email protected] 
    FOR XML PATH('') 
),1,1,'') AS BookingInfoCSV; 
GO 
!

- Indice l'astuce avec XQuery data() function va casser, si vos valeurs contiennent des espaces!

--Le suivant SELECT appels toutes les lignes de Booking -table et obtient les détails de montage

SELECT b.BookingID 
     ,b.SomeBookingData 
     ,A.BookingInfoCSV 
FROM Booking AS b 
OUTER APPLY dbo.CreateBookingInfoCSV(b.BookingID) AS A; 
GO 

--clean up (avec des données réelles carefull!)

USE master; 
GO 
DROP DATABASE testDB; 

--Le résultat

BookingID SomeBookingData BookingInfoCSV 
1   Booking 1  AP,AP,XY,MN 
2   Booking 2  AA,BB 
+0

Hey @Shnugo, cette solution fonctionne bien, sauf s'il y a un espace dans ttribute valeur i.e si xml arrive comme ceci AP nous obtenons le sortir mis comme AP, s'il y a deux noeuds AP ,, AP, Veuillez corriger. –

+0

Si vous n'avez pas d'espaces * dans les codes, vous pouvez envelopper tout le résultat avec 'REPLACE (A.BookingInfoCSV, ',,', ',')' pour remplacer la double virgule par une simple virgule. Pour être honnête: Ceci est un (bon) hack, mais c'est un hack ... – Shnugo

+0

Hola @shnugo .. Mais si la valeur est (espace après AP) AP nous obtenons l'out mis comme AP, –