2011-07-21 2 views
3

J'ai quelques (typées) XML étant stockées dans SQL Server 2005 que je dois transformer en une structure normalisée. La structure du document ressemble actuellement à ceci:Utiliser SQL Server 2005 API XML pour normaliser un fragment XML

<wrapper> 
<parent /> 
<node /> 
<node /> 
<node /> 

<parent /> 
<node /> 
<node /> 
<node /> 
<wrapper> 

Je veux la transformer pour ressembler à ceci:

<wrapper> 
<parent> 
    <node /> 
    <node /> 
    <node /> 
</parent> 
<parent> 
    <node /> 
    <node /> 
    <node /> 
</parent> 
<wrapper> 

je peux sélectionner le XML à en une structure relationnelle si j'ai besoin de, mettre le problème est qu'il n'y a pas d'attributs reliant le parent et les nœuds enfants ensemble, de sorte que l'ordre devient un problème quand usin g les opérations basées sur un ensemble. Comment puis-je utiliser le .nodes() /. Value()/autres API XML SQL Server pour transformer ces données? La transformation doit être exécutée dans le cadre d'un script SQL par lots, donc l'extraire dans un autre outil/langue n'est pas une option raisonnable pour moi.

+1

Je doute fortement que vous puissiez faire quelque chose comme cela avec un effort raisonnable dans SQL Server. Son moteur XQuery est idéal pour interroger le XML et en extraire des données, moins pour lever plus lourdement du côté de la manipulation. Je pense que vous seriez mieux de saisir ce XML de SQL Server dans e.g. C# /. NET et faire la fixation là-bas et le stocker. –

Répondre

1

En fait - le code suivant fonctionne (regroupement ici peut être est pas très optimale, mais de toute façon):

declare @xml xml = ' 
    <wrapper> 
    <parent id="1" /> 
    <node id="1" /> 
    <node id="2" /> 
    <node id="3" /> 

    <parent id="2" /> 
    <node id="4" /> 
    <node id="5" /> 
    <node id="6" /> 
    </wrapper> 
' 

;with px as 
(
    select row_number() over (order by (select 1)) as RowNumber 
     ,t.v.value('@id', 'int') as Id 
     ,t.v.value('local-name(.)', 'nvarchar(max)') as TagName 
    from @xml.nodes('//wrapper/*') as t(v) 
) 
select p.Id as [@id], 
    (
     select n.Id as id 
     from px n 
     where n.TagName = 'node' 
      and n.RowNumber > p.RowNumber 
      and not exists 
      (
       select null 
       from px np 
       where np.TagName = 'parent' 
        and np.RowNumber > p.RowNumber 
        and np.RowNumber < n.RowNumber 
      ) 
     order by n.RowNumber 
     for xml raw('node'), type 
    ) 
from px p 
where p.TagName = 'parent' 
order by p.RowNumber 
for xml path('parent'), root('wrapper')

Mais je ne recommande pas l'utiliser. Voir ici: http://msdn.microsoft.com/en-us/library/ms172038%28v=sql.90%29.aspx:

Dans SQLXML 4.0, l'ordre du document n'est pas toujours déterminé
Je ne suis pas sûr que nous pouvons compter sur l'ordre des balises à l'intérieur wrapper (et code ci-dessus est plus juste pour le plaisir que pour une utilisation pratique).

+0

Merci pour l'entrée, je suis arrivé à la même conclusion - ce n'est pas déterministe. Je vais trouver autre chose. –

+0

@ Jonathan - Vous pouvez utiliser 'la position()' avec une table de numéros d'énumérer les lignes d'une manière déterministe garantie. Jetez un oeil à cette réponse. http://stackoverflow.com/questions/6472533/xquery-and-node-ids/6473228#6473228 –

+1

Excellente solution! J'ai essayé d'utiliser la position() en fonction de la valeur, mais il n'a pas aidé (position peut être utilisé qu'à l'intérieur prédicats). – oryol