切碎的xml排名

时间:2009-10-07 16:24:18

标签: sql sql-server sql-server-2005 tsql sqlxml

鉴于以下XML示例和将{xml分解为关系的select语句,我需要的是select的第二列作为类别的序数< / strong>(例如,1表示方向,2表示颜色)。

注意:select中的文字值'rank()'留有占位符。我正在使用rank,但没有成功。

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)

4 个答案:

答案 0 :(得分:2)

Jacob Sebastian在他的博客文章中也有一个有趣的解决方案:

XQuery Lab 23 - Retrieving values and position of elements

根据Jacob的建议,我可以将您的查询重写为:

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'

我得到了所需的输出:

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

不幸的是,position()fn:id()函数等更明显的解决方案似乎a)在SQL Server中工作或b)在SQL Server中完全支持: - (

希望这有帮助

马克

答案 1 :(得分:1)

也许是这样的:你得到每个类别的第一个元素并将其用作id。

此:

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

返回:

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

然后只是

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)

然而它返回:

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

但它仍然领先一步。

答案 2 :(得分:1)

您不能使用position()生成输出(为什么??),但您可以将其用作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);

您可以使用实数表来获得更高的排名。对于单个文档中的大量类别来说效率不高,但对于中等数量(数十,数百),可以正常工作。

答案 3 :(得分:0)

与Lukasz的答案类似,我能够通过以下方式获得理想的结果:

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

它返回:

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