2014-05-12 2 views
0

Je suis supposé extraire des données d'une colonne XML dans sql server 2012. Certaines valeurs de cette colonne auront plusieurs nœuds. Malheureusement, le XML n'a pas de nœud racine, donc utiliser CROSS APPLY ne semble pas fonctionner.Requête (colonne) dépourvue de nœud racine

Exemple simplifié:

<header><msg_type>TYPE_ONE</msg_type> 
<status><status_1>aaaa</status_1><status_2>bbbb</status_2> 
<node_1><customerID>1234</customerID><zipcode>11111</zipcode>...</node_1> 
<node_2><customerID>1234</customerID><ordernum>12345</ordernum><data2>A</ordernum>...</node_2> 
<node_2><customerID>1234</customerID><ordernum>34567></ordernum><data2>B</ordernum>...</node_2> 
<node_3><customerID>1234</customerID><delivery>2014-05-05 14:00:00></delivery>...</node_3> 
<node_1><customerID>ABCD</customerID><zipcode>12345</zipcode>...</node_1> 
<node_2><customerID>ABCD</customerId><ordernum>123536</ordernum><data2>C</ordernum>...</node_2> 
<node_3><customerID>ABCD</customerID><delivery>2014-05-05 16:00:00>...</node_3> 

. . .

(... = plus d'éléments)

Voici un exemple en utilisant CROSS APPLY contre l'un des types multi-nœuds:

select 
    t.InfoXML.value('/node_1/customerID)[1]', 'varchar(50)') as CustomerId 
    , CA.Det.value('(/node_2/ordernum)[1]', 'varchar(20)') as OrderNumber 
    , CA.Det.value('(/node_2/data2)[1]', 'varchar(5)' ) as Data2 
from TableUnderTest as t 
CROSS APPLY t.InfoXML.nodes('/node_2') as CA(Det) 
where t.InfoXML.value('(/header/msg_type)[1]', 'varchar(20)') = 'TYPE_ONE' 

Cela a eu des résultats

CustomerID OrderNumber Data2 
================================ 
1234   12345  A 
1234   12345  A 
1234   12345  A 

Mon actuelle pensé est de créer une table temporaire et insérer les champs XML (qui correspondent à la clause WHERE) après avoir enveloppé la valeur XML dans un nœud racine, puis d'essayer d'obtenir les données de la table temporaire. Mon effort actuel pour mettre la première partie en place:

Declare @Rooted Table (Rec XML); 
insert into @Rooted(Rec) 
(
    select (convert (XML, '&lt;root&gt;', + convert(varchar(MAX), 
    t.XmlData.query('./') + '&lt;\root&gt;')) as Rec 
    from TableUnderTest t 
    where t.XmlData.value('(/header/msg_type)[1]', 'varchar(20)') = 'TYPE_ONE' 
) 

À l'heure actuelle, ce qui précède donne une erreur de syntaxe.

Ce que je veux pour la sortie est quelque chose comme suit:

CustomerID ZipCode OrderNumber Data2 Delivery   status2 
------------------------------------------------------------------- 
1234   11111 12345  A  2014-05-05 14:00:00 aaaa  
1234   11111 34567  B  2014-05-05 14:00:00 aaaa 
ABCD   12345 123456 D  2014-05-05 15:00:00 aaaa 

Quelle serait la meilleure approche à prendre? (Ceci est pour les tests, pas pour la production, donc les performances ne sont pas critiques.) J'ai seulement appris à écrire des requêtes sql pour XML pour le mois dernier, donc peut-être que je néglige quelque chose. Il semble que le problème critique est l'absence d'un nœud racine pour le XML, mais comment puis-je contourner ce problème?

+0

Pourriez-vous prouver une donnée XML valide à partir de laquelle vous voulez obtenir la sortie désirée? – gotqn

Répondre

0

Vous pouvez commencer à partir de la requête suivante:

DECLARE @x XML; 
SET @x = N'<header><msg_type>TYPE_ONE</msg_type></header> 
<status><status_1>aaaa</status_1><status_2>bbbb</status_2></status> 
<node_1><customerID>1234</customerID><zipcode>11111</zipcode></node_1> 
<node_2><customerID>1234</customerID><ordernum>12345</ordernum></node_2> 
<node_2><customerID>1234</customerID><ordernum>34567</ordernum></node_2> 
<node_3><customerID>1234</customerID><delivery>2014-05-05 14:00:00</delivery></node_3> 
<node_1><customerID>ABCD</customerID><zipcode>12345</zipcode></node_1> 
<node_2><customerID>ABCD</customerID><ordernum>123536</ordernum></node_2> 
<node_3><customerID>ABCD</customerID><delivery>2014-05-05 16:00:00</delivery></node_3>'; 

SELECT * 
FROM 
(
    SELECT a.ElementName, 
      a.CustomerID, 
      a.OrderNum, 
      ZipCode  = MAX(a.ZipCode) OVER(PARTITION BY a.CustomerID), 
      Delivery = MAX(a.Delivery) OVER(PARTITION BY a.CustomerID) 
    FROM 
    (
     SELECT RowNum  = ROW_NUMBER() OVER(ORDER BY y.XmlCol), 
       ElementName = y.XmlCol.value('local-name(.)', 'SYSNAME'), 
       CustomerID = y.XmlCol.value('(customerID/text())[1]', 'VARCHAR(50)'), 

       ZipCode  = y.XmlCol.value('(zipcode/text())[1]', 'VARCHAR(50)'), 
       OrderNum = y.XmlCol.value('(ordernum/text())[1]', 'VARCHAR(50)'), 
       Delivery = y.XmlCol.value('(delivery/text())[1]', 'VARCHAR(50)') 
     FROM @x.nodes('/') x(XmlCol) 
     CROSS APPLY x.XmlCol.nodes('/*[local-name() = "node_1" or local-name() = "node_2" or local-name() = "node_3"]') y(XmlCol) 
     WHERE x.XmlCol.exist('(header/msg_type)[text() = "TYPE_ONE"]') = 1 
    ) a 
) b 
WHERE b.ElementName = 'node_2' 

sortie:

ElementName CustomerID OrderNum ZipCode Delivery 
----------- ---------- -------- ------- ------------------- 
node_2  1234  12345 11111 2014-05-05 14:00:00 
node_2  1234  34567 11111 2014-05-05 14:00:00 
node_2  ABCD  123536 12345 2014-05-05 16:00:00 

J'ai fait les hypothèses suivantes:

  • Chaque a un élément de node_1, un ou plusieurs éléments de node_2 et un élément node_3
  • Le node_1 éléments ont des sous-éléments,
  • Les node_2 éléments ont des sous-éléments et
  • Les node_3 éléments ont des sous-éléments.

résultats intermédiaires (si vous exécutez uniquement la table dérivée (...) a/sous-requête vous obtiendrez ces résultats):

ElementName CustomerID ZipCode OrderNum Delivery 
----------- ---------- ------- -------- ------------------- 
node_1  1234  11111 NULL  NULL 
node_2  1234  NULL 12345 NULL 
node_2  1234  NULL 34567 NULL 
node_3  1234  NULL NULL  2014-05-05 14:00:00 
node_1  ABCD  12345 NULL  NULL 
node_2  ABCD  NULL 123536 NULL 
node_3  ABCD  NULL NULL  2014-05-05 16:00:00 

S'il vous plaît noter que je ne faisais pas des tests de performance. Remplacez également les types de données par {x|y}.XmlCol.value(...,'type') avec le type correct et max. longueur/précision/échelle.

+0

Merci! Cela fonctionne définitivement! Question connexe, cependant - comment puis-je le mieux configurer dans une requête par rapport à une table complète (avec la clause where pour sélectionner le type de message)? Je suppose que je pourrais définir cela comme une fonction temporaire que la requête principale appellerait ... quelle serait la manière la plus simple d'établir ceci? – LindaP

Questions connexes