sql - 从具有图形内容的 Xml 中选择所有路径

标签 sql xml sql-server-2008 xquery

我有一个包含如下元素的 XML 列:

<Root>
    <Word Type="pre1" Value="A" />
    <Word Type="pre1" Value="D" />

    <Word Type="base" Value="B" />

    <Word Type="post1" Value="C" />
    <Word Type="post1" Value="E" />
    <Word Type="post1" Value="F" />
</Root>

那个模型是这样的:

enter image description here

并希望在 MSSQL 中使用 XQuery 选择所有可能的路径以获得类似这样的结果:

ABC ABE ABF DBC DBE DBF

或者类似的东西:

<Root>
    <Word Type="pre1" Value="A" />
    <Word Type="pre1" Value="D" />

    <Word Type="pre2" Value="G" />
    <Word Type="pre2" Value="H" />

    <Word Type="base" Value="B" />

    <Word Type="post1" Value="C" />
    <Word Type="post1" Value="E" />
    <Word Type="post1" Value="F" />
</Root>

enter image description here

结果是:

AHBC AHBE AHBF DHBC DHBE DHBF AGBC AGBE AGBF DGBC DGBE DGBF

最佳答案

您可以使用 CTE 构建唯一类型列表,然后在递归 CTE 中使用它来构建字符串。最后,您挑选出上次迭代中生成的字符串。

with Types as
(
  select row_number() over(order by T.N) as ID,
         T.N.value('.', 'varchar(10)') as Type
  from (select @XML.query('for $t in distinct-values(/Root/Word/@Type) 
                           return <T>{$t}</T>')
       ) as X(T)
    cross apply X.T.nodes('/T') as T(N)
),
Recu as
(
  select T.Type,
         T.ID,
         X.N.value('@Value', 'varchar(max)')  as Value
  from Types as T
    cross apply @XML.nodes('/Root/Word[@Type=sql:column("T.Type")]') as X(N)
  where T.ID = 1
  union all
  select T.Type,
         T.ID,
         R.Value+X.N.value('@Value', 'varchar(max)') as Value
  from Types as T
    inner join Recu as R
      on T.ID = R.ID + 1
    cross apply @XML.nodes('/Root/Word[@Type=sql:column("T.Type")]') as X(N)    
)
select R.Value
from Recu as R
where R.ID = (select max(T.ID) from Types as T)
order by R.Value

SQL Fiddle

更新

这是一个性能更好的版本。它将 XML 分解为两个临时表。每种类型一个,所有单词一个。仍然需要递归 CTE,但它使用表而不是 XML。 CTE 中的连接使用的每个临时表上还有一个索引。

-- Table to hold all values
create table #Values
(
  Type varchar(10),
  Value varchar(10)
);

-- Clustered index on Type is used in the CTE
create clustered index IX_#Values_Type on #Values(Type)

insert into #Values(Type, Value)
select T.N.value('@Type', 'varchar(10)'),
       T.N.value('@Value', 'varchar(10)')
from @XML.nodes('/Root/Word') as T(N);

-- Table that holds one row for each Type
create table #Types
(
  ID int identity,
  Type varchar(10),
  primary key (ID)
);

-- Add types by document order
-- Table-Valued Function Showplan Operator for nodes guarantees document order
insert into #Types(Type)
select T.Type
from (
     select row_number() over(order by T.N) as rn,
            T.N.value('@Type', 'varchar(10)') as Type
     from @XML.nodes('/Root/Word') as T(N)
     ) as T
group by T.Type
order by min(T.rn);

-- Last level of types
declare @MaxID int;
set @MaxID = (select max(ID) from #Types);

-- Recursive CTE that builds the strings
with C as 
(
  select T.ID,
         T.Type,
         cast(V.Value as varchar(max)) as Value
  from #Types as T
    inner join #Values as V
      on T.Type = V.Type
  where T.ID = 1
  union all
  select T.ID,
         T.Type,
         C.Value + V.Value
  from #Types as T
    inner join C
      on T.ID = C.ID + 1
    inner join #Values as V
      on T.Type = V.Type
)
select C.Value
from C
where C.ID = @MaxID
order by C.Value;

-- Cleanup
drop table #Types;
drop table #Values;

SQL Fiddle

关于sql - 从具有图形内容的 Xml 中选择所有路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16185977/

相关文章:

sql - 您可以将多个列用于不在查询中吗?

sql - 检查存储过程中参数是否为 null 或为空

sql - 在不使用不同运算符的情况下比较 Sql 中的行? (不同的运营商实现)

c# - 从 XML 序列化数组中删除包装元素

java - JAXB - 将节点添加到 XML 作为 html 链接

sql - 正确计算 2 个日期范围之间的时间

sql - 高效的SQL统计最近X行中的事件

sql - 即使使用白名单 IP,也无法连接到 Azure SQL 数据库

MySQL JOIN UNION 的结果

java - 更改 jax-b 实现