it-swarm.com.de

T-SQL importiert XML dynamisch in Tabellen (SQL Server 2014)

Ich habe eine XML-Antwort mit dieser Struktur, aber mit ungefähr 150 verschiedenen Knoten:

<?xml version="1.0" encoding="UTF-8"?>
<Orders>
    <Order>
        <OrderID>334</OrderID>
        <AmountPaid currencyID="EUR">17.10</AmountPaid>
        <UserID>marc58</UserID>
        <ShippingAddress>
            <Name>Marc Juppé</Name>
            <Address>Rue</Address>
            <City>Paris</City>
            <StateOrProvince></StateOrProvince>
            <Country>FR</Country>
            <Phone>333333333</Phone>
            <PostalCode>22222</PostalCode>
         </ShippingAddress>
         <ShippingCosts>4.50</ShippingCosts>
         <Items>
            <Item>
               <Details>
                    <ItemID>3664</ItemID>
                    <Store>47</Store>
                    <Title>MCPU DDA010</Title>
                    <SKU>mmx</SKU>
                </Details>
                <Quantity>1</Quantity>
                <Price currencyID="EUR">6.2</Price>
            </Item>
            <Item>
               <Details>
                    <ItemID>3665</ItemID>
                    <Store>45</Store>
                    <Title>MCPU DFZ42</Title>
                    <SKU>mmy</SKU>
                </Details>
                <Quantity>2</Quantity>
                <Price currencyID="EUR">3.2</Price>
            </Item>
        </Items>
    </Order>
</Orders>

Ich muss diese Informationen in 3 verschiedenen Tabellen speichern und für Item Tabelle muss ich einen Datensatz für jede unterschiedliche <Item>, aber auch das Einfügen der Reihenfolge Node details; wie folgt:

|ItemID|Store|Title |SKU|Quantity|Price|OrderID|AmountPaid|UserID|ShippingCost|
|3664  |   47|DDA010|mmx|       1|  6.2|    334|     17.10|marc58|        4.50|
|3665  |   45|DFZ42 |mmy|       2|  3.2|    334|     17.10|marc58|        4.50|

Um die erforderlichen Informationen "automatisch" in die verschiedenen Tabellen zu schreiben, habe ich mit großer Hilfe der Community diese Abfrage erstellt:

Set @T1='Orders'
Set @F1='OrderID'
Set @V=''

    SELECT 
        @C= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@C)=0, CONCAT( ISNULL(@C + ',','') , QUOTENAME(T.X.value('local-name(.)', 'nvarchar(100)'))), @C),
        @D= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@CP)=0, CONCAT( ISNULL(@D + ',N','') , '''',  T.X.value(N'text()[1]', 'nvarchar(max)'),''''), @D),
        @U= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@CP)=0, CONCAT( ISNULL(@U + ',','') , QUOTENAME( T.X.value('local-name(.)', 'nvarchar(100)')) ,'=', '''',T.X.value(N'text()[1]', 'nvarchar(max)'),''''), @U),
        @V= IIF(T.X.value('local-name(.)', 'nvarchar(100)') [email protected], T.X.value('text()[1]', 'nvarchar(100)'), @V), 
    FROM @XML.nodes('//*[count(child::*) = 0]') AS T(X)
    WHERE  T.X.value(N'local-name(.)', 'nvarchar(500)') 
    IN (select name from db1.sys.columns where [object_id]=OBJECT_ID(@T1)and is_identity=0)

    SELECT @C = STUFF(@C, 1, 1, '');
    SELECT @D = STUFF(@D, 1, 1, '');
    SELECT @U = STUFF(@U, 1, 1, '');

    SET @S=N'IF NOT EXISTS (SELECT 1 FROM '[email protected]+' WHERE '[email protected]+' = '''[email protected]+''') 
             INSERT INTO '[email protected]+' ('[email protected]+') VALUES ('[email protected]+''') 
             ELSE UPDATE '[email protected]+' SET '[email protected]+''' WHERE '[email protected]+'='''[email protected]+''''

    Print @S

    EXEC sp_executesql .....

Set @T1="Users"

......

OK, besser hier eine Schleife verwenden

Obwohl diese Abfrage sicherlich verbessert und optimiert werden kann, hat sie bisher gut funktioniert, da es nur einen Artikelknoten gab, aber jetzt gibt sie mit mehr Artikelknoten nur den ersten zurück.

Ich habe versucht, die FROM-Klausel zu ändern, indem ich versucht habe, auf die Elementauflistung zu verweisen, aber ohne Erfolg, aber ich denke, dass ich keine Ahnung habe, wie ich die Reihenfolge erhalten soll, wenn ich die Elementknoten iteriere Node Details, die Eltern des Item-Knotens sind ...

Können Sie eine Lösung vorschlagen?

Vielen Dank

5
Joe

Die Abfrage, die Sie von Forrest erhalten haben kann etwas verbessert werden.

Die Verwendung der übergeordneten Achse ist in xQuery in SQL Server fast immer eine wirklich schlechte Idee . Sie können dies vermeiden, indem Sie zuerst Orders/Order Vernichten und dann ein Kreuz anwenden, um Items/Item Zu vernichten.

Auch die Verwendung von query('').value('.') ist keine gute Idee. Stellen Sie mit [1] Besser sicher, dass Sie nur einen Wert aus der Funktion value() erhalten.

Eine zusätzliche Sache für die Leistung ist die Angabe des Knotens text() in value().

select I.X.value('(Details/ItemID/text())[1]', 'int') as ItemID,
       I.X.value('(Details/Store/text())[1]', 'int') as Store,
       I.X.value('(Details/Title/text())[1]', 'nvarchar(100)') as Title,
       I.X.value('(Details/SKU/text())[1]', 'nvarchar(100)') as SKU,
       I.X.value('(Quantity/text())[1]', 'int') as Quantity,
       I.X.value('(Price/text())[1]', 'decimal(11,2)') as Quantity,
       O.X.value('(OrderID/text())[1]', 'int') as OrderID,
       O.X.value('(AmountPaid/text())[1]', 'decimal(11,2)') as AmountPaid,
       O.X.value('(UserID/text())[1]', 'nvarchar(100)') as UserID,
       O.X.value('(ShippingCosts/text())[1]', 'decimal(11,2)') as ShippingCosts
from @XML.nodes('/Orders/Order') as O(X)
  cross apply O.X.nodes('Items/Item') as I(X);
4
Mikael Eriksson

Schlechte Nachrichten: Der Ansatz local-name(.) plus @XML.nodes('//*[count(child::*) = 0]') in Ihrem Skript glättet das XML-Dokument vollständig und muss überarbeitet werden, wenn Sie mehrere X-pro-Y-Strukturen in Ihrem Skript haben dokumentieren. Wenn dynamisches SQL erforderlich ist, geben Sie bitte ein Beispiel an, um diesen Aspekt einfacher zu testen.

Ein anderer Ansatz könnte darin bestehen, Abfragen manuell zu erstellen. Wenn Ihr grundlegendes Problem darin besteht, übergeordnete Informationen zu enthalten, können Sie meine SELECT-Demo-Abfrage unten ändern.

( N.B. Mikael Erikssons Antwort hat eine verbesserte Abfrage. Bitte beziehen Sie sich darauf.)

Schlüsselideen:

. Ist der Kontextknoten und .. Gibt Ihnen den übergeordneten Knoten an

.nodes Ist eine andere Art von XML-Funktion, die in den FROM-Abschnitt der Abfrage gehört und normalerweise mit CROSS APPLY angezeigt wird. Es gibt einen Zeiger pro Übereinstimmung zurück und ermöglicht das Arbeiten mit mehreren Zeilen. Lesen Sie hier mehr.

.query().value ist eine von mehreren Methoden, um SQL Server wissen zu lassen, dass die Wertemethode nur ein Datenelement enthält, mit dem gearbeitet werden kann (Behebung des Fehlers "erfordert einen Singleton")

DECLARE @XML xml = 
'<Orders>
    <Order>
        <OrderID>334</OrderID>
        <AmountPaid currencyID="EUR">17.10</AmountPaid>
        <UserID>marc58</UserID>
        <ShippingAddress>
            <Name>Marc Juppé</Name>
            <Address>Rue</Address>
            <City>Paris</City>
            <StateOrProvince></StateOrProvince>
            <Country>FR</Country>
            <Phone>333333333</Phone>
            <PostalCode>22222</PostalCode>
         </ShippingAddress>
         <ShippingCosts>4.50</ShippingCosts>
         <Items>
            <Item>
               <Details>
                    <ItemID>3664</ItemID>
                    <Store>47</Store>
                    <Title>MCPU DDA010</Title>
                    <SKU>mmx</SKU>
                </Details>
                <Quantity>1</Quantity>
                <Price currencyID="EUR">6.2</Price>
            </Item>
            <Item>
               <Details>
                    <ItemID>3665</ItemID>
                    <Store>45</Store>
                    <Title>MCPU DFZ42</Title>
                    <SKU>mmy</SKU>
                </Details>
                <Quantity>2</Quantity>
                <Price currencyID="EUR">3.2</Price>
            </Item>
        </Items>
    </Order>
</Orders>'

SELECT 
    x.value('./ItemID[1]','int') AS ItemID,
    x.value('./Store[1]','int') AS Store,
    x.value('./Title[1]','nvarchar(100)') AS Title,
    x.value('./SKU[1]','nvarchar(100)') AS SKU,
    x.value('../Quantity[1]','int') AS Qty,
    x.value('../Price[1]','decimal(11,2)') AS Price,
    x.query('//OrderID[1]').value('.','int') AS OrderID,
    x.query('//AmountPaid[1]').value('.','decimal(11,2)') AS AmountPaid,
    x.query('//UserID[1]').value('.','nvarchar(100)') AS UserID,
    x.query('//ShippingCosts[1]').value('.','decimal(11,2)') AS ShippingCosts
FROM @XML.nodes('//Item/Details') i(x)
7
Forrest

Versuchen wir, eine temporäre denormalisierte Tabelle mit all Ihren Daten zu erstellen. Einige Felder werden verworfen, aber Sie können sie einfach selbst hinzufügen. Sie können in Ihre Produktionstabellen Gruppierungsergebnisse nach Ihrem eigenen Geschmack einfügen.

    declare @xml varchar(max)
    declare @idoc int

    set @xml='<?xml version="1.0" encoding="UTF-8"?>
    <Orders>
        <Order>
            <OrderID>334</OrderID>
            <AmountPaid currencyID="EUR">17.10</AmountPaid>
    <UserID>marc58</UserID>
    <ShippingAddress>
        <Name>Marc Juppé</Name>
        <Address>Rue</Address>
        <City>Paris</City>
        <StateOrProvince></StateOrProvince>
        <Country>FR</Country>
        <Phone>333333333</Phone>
        <PostalCode>22222</PostalCode>
     </ShippingAddress>
     <ShippingCosts>4.50</ShippingCosts>
     <Items>
        <Item>
           <Details>
                <ItemID>3664</ItemID>
                <Store>47</Store>
                <Title>MCPU DDA010</Title>
                <SKU>mmx</SKU>
            </Details>
            <Quantity>1</Quantity>
            <Price currencyID="EUR">6.2</Price>
        </Item>
        <Item>
           <Details>
                <ItemID>3665</ItemID>
                <Store>45</Store>
                <Title>MCPU DFZ42</Title>
                <SKU>mmy</SKU>
            </Details>
            <Quantity>2</Quantity>
            <Price currencyID="EUR">3.2</Price>
        </Item>
    </Items>
        </Order>
    </Orders>'

    print 'prepare xml document'
    exec sp_xml_preparedocument @idoc output, @xml

    print 'create temporary table'
    CREATE TABLE #import (OrderID int, UserID varchar(500), AmountPaid numeric(18,2), AmountPaidCurrencyID char(3),  ShippingAddress_Name varchar(500), ShippingCosts numeric(18,2), ItemID int, Store int, Title varchar(500), SKU varchar(500), Quantity numeric(18,4), CurrencyID char(3), CurrencyPrice numeric(18,2))
    CREATE INDEX #ix_import ON #import (OrderID)

    print 'insert temporary table'
    insert into #import (  OrderID,  UserID,  AmountPaid,  AmountPaidCurrencyID,  ShippingAddress_Name,  ShippingCosts,  ItemID,  Store,  Title,  SKU,  Quantity,  CurrencyID,  CurrencyPrice)
    SELECT               a.OrderID,a.UserID,a.AmountPaid,a.AmountPaidCurrencyID,a.ShippingAddress_Name,a.ShippingCosts,b.ItemID,b.Store,b.Title,b.SKU,b.Quantity,b.CurrencyID,b.CurrencyPrice
    FROM        OPENXML (@idoc, '/Orders/Order',2)
                WITH    (
                         OrderID int                        'OrderID'
                        ,UserID varchar(500)                'UserID'
                        ,AmountPaidCurrencyID char(3)       'AmountPaid/@currencyID'
                        ,AmountPaid numeric(18,2)           'AmountPaid'
                        ,ShippingAddress_Name varchar(500)  'ShippingAddress/Name'
                        ,ShippingCosts numeric(18,2)        'ShippingCosts'
                        ) a

    LEFT JOIN   OPENXML (@idoc, '/Orders/Order/Items/Item',2)
                WITH    (
                         OrderID int                '../../OrderID'
                        ,ItemID int                 'Details/ItemID'
                        ,Store int                  'Details/Store'
                        ,Title varchar(500)         'Details/Title'
                        ,SKU varchar(500)           'Details/SKU'
                        ,Quantity numeric(18,4)     'Quantity'
                        ,CurrencyID char(3)         'Price/@currencyID'
                        ,CurrencyPrice numeric(18,2)'Price'
                        ) b ON a.OrderID=b.OrderID

    print 'remove xml document'
    exec sp_xml_removedocument @idoc

    -- 
    select * from #import

    print 'drop temporary table'
    DROP TABLE #import

Hier ist die Ergebnismenge:

    OrderID UserID  AmountPaid  AmountPaidCurrencyID    ShippingAddress_Name    ShippingCosts   ItemID  Store   Title       SKU Quantity    CurrencyID  CurrencyPrice
    334     marc58  17.10       EUR                     Marc Juppe              4.50            3664    47      MCPU DDA010 mmx 1.0000      EUR         6.20
    334     marc58  17.10       EUR                     Marc Juppe              4.50            3665    45      MCPU DFZ42  mmy 2.0000      EUR         3.20
4
QuickJoe