2009-10-07 5 views
3

déchiqueté Étant donné l'exemple suivant de XML et la déclaration select qui déchiquettent le xml dans une relation, ce j'ai besoin est la deuxième colonne du select être le ordinale de la catégorie (c.-à-1 pour les directions et 2 pour les couleurs dans ce cas).sur xml

Remarque: La valeur littérale 'rank()' dans la sélection est laissée comme espace réservé. Je farfouillais avec l'utilisation du rank, mais sans succès.

declare @x xml 
set @x = ' 
    <root> 
     <category> 
      <item value="north"/> 
      <item value="south"/> 
      <item value="east"/> 
      <item value="west"/> 
     </category> 
     <category> 
      <item value="red"/> 
      <item value="green"/> 
      <item value="blue"/> 
     </category> 
    </root>' 

select c.value('./@value', 'varchar(10)') as "ItemValue", 
     'rank()' as "CategoryNumber" 
from @x.nodes('//item') as t(c) 
+0

Si seulement je pouvais trouver comment utiliser « fn: id » ... http://msdn.microsoft.com/en-us/library/ms190915.aspx – gbn

+0

@marc_s --unfortunately le xml est ce qu'il est --- je ne peux pas le changer. –

Répondre

2

Jacob Sebastian a également une solution intéressante présentée dans son blog:

XQuery Lab 23 - Retrieving values and position of elements

Avec la suggestion de Jacob, je peux réécrire votre requête à:

SELECT 
    x.value('@value','VARCHAR(10)') AS 'ItemValue',   
    p.number as 'CategoryNumber' 
FROM 
    master..spt_values p 
CROSS APPLY 
    @x.nodes('/root/category[position()=sql:column("number")]/item') n(x) 
WHERE 
    p.type = 'p' 

et je reçois désiré sortie:

ItemValue CategoryNumber 
--------- -------------- 
north   1 
south   1 
east   1 
west   1 
red    2 
green   2 
blue   2 

Malheureusement, aucun des travaux plus de solutions évidentes comme les semblent a) position() ou fn:id() fonctions dans SQL Server ou b) être pris en charge dans SQL Server à tous :-(

Hope this helps

Marc

+0

Donc * ceci * est comment vous le faites ... – gbn

1

Peut-être comme ceci: vous obtenez le premier élément de chaque catégorie et l'utilisez comme identifiant.

ceci:

select c.value('./@value', 'varchar(10)') as "ItemValue", 
    c.value('../item[1]/@value', 'varchar(10)') as "CategoryNumber" 
from @x.nodes('//item') as t(c) 

Retours:

Item Value | CategoryNumber 
--------------------------- 
north  | north 
south  | north 
east  | north 
west  | north 
red  | red 
green  | red 
blue  | red 

Et puis juste

select c.value('./@value', 'varchar(10)') as "ItemValue", 
    RANK() OVER (ORDER BY c.value('../item[1]/@value', 'varchar(10)')) as "CategoryNumber" 
from @x.nodes('//item') as t(c) 

Il retourne cependant:

Item Value | CategoryNumber 
--------------------------- 
north  | 1 
south  | 1 
east  | 1 
west  | 1 
red  | 5 
green  | 5 
blue  | 5 

Mais il est encore en avance.

+0

Comme il s'avère que cela fonctionnera très bien. Je dois avoir la sortie groupée par catégorie avec une valeur arbitraire, mais elle ne doit pas être contiguë. –

1

Vous ne pouvez pas utiliser position() pour produire une sortie (pourquoi ??), mais vous pouvez l'utiliser comme filtres XPath:

with numbers (n) as (
    select 1 
    union all select 2 
    union all select 3 
    union all select 4 
    union all select 5) 
select i.x.value('@value', 'varchar(10)') as [ItemValue], 
    n.n as [rank] 
    from numbers n 
    cross apply @x.nodes('/root/category[position()=sql:column("n.n")]') as c(x) 
    cross apply c.x.nodes('item') as i(x); 

Vous pouvez utiliser une vraie table de nombres pour les grades plus élevés. Pour un très grand nombre de catégories dans un seul document ne sera pas efficace, mais pour des nombres modérés (des dizaines, des centaines) fonctionnera très bien.

+0

Je suis content que vous ayez compris cela; J'essayais de trouver quelque chose de similaire, mais échouant misérablement. –

0

similaires à réponse Lukasz, j'ai pu obtenir le résultat souhaité avec:

SELECT 
    I.Item_Instance.value('@value','VARCHAR(10)') AS Item_Value, 
    DENSE_RANK() OVER (ORDER BY C.Category_Instance) AS Category_Ordinal 
FROM @x.nodes('/root') AS R(Root_Instance) 
CROSS APPLY R.Root_Instance.nodes('category') AS C(Category_Instance) 
CROSS APPLY C.Category_Instance.nodes('item') AS I(Item_Instance); 

Il retourne:

Item_Value Category_Ordinal 
---------- -------------------- 
north  1 
south  1 
east  1 
west  1 
red  2 
green  2 
blue  2 
Questions connexes