【问题标题】:Transformation of XML tree to flat text using SQL Server 2008 XQuery使用 SQL Server 2008 XQuery 将 XML 树转换为平面文本
【发布时间】:2011-04-20 19:28:09
【问题描述】:

我有一些表示数学表达式树的 XML 数据,并希望将其转换为平面数学公式。听起来很简单,但 SQL Server 中的 XQuery 限制目前阻止了我成功(没有递归函数、“异构”结果的问题等)。

表达式可以是任意嵌套深度。这是一个示例(数据稍后会在表格的 xml 列中,但这足以在这里进行测试):

DECLARE @expr xml;
SET @expr = '<expression aggregator="+">
  <indicator>122F277B-A241-7944-BC38-3BB5E8B213AF</indicator>
  <indicator>7DD46849-2193-EB41-8BAB-CE0C45255249</indicator>
  <expression aggregator="*">
    <expression aggregator="/">
      <indicator>122F277B-A241-7944-BC38-3BB5E8B213AF</indicator>
      <indicator>27F3156D-FDA7-1E44-B545-7F27A48D9838</indicator>
    </expression>
    <indicator>ADFCEF34-9877-DE4E-8A00-13576437D82B</indicator>
    <value>12</value>
  </expression>
  <expression aggregator="-">
    <indicator>ADFCEF34-9877-DE4E-8A00-13576437D82B</indicator>
    <indicator>75896474-C197-1C44-8EAA-8FE9D0AB2663</indicator>
  </expression>
  <indicator>27F3156D-FDA7-1E44-B545-7F27A48D9838</indicator>
</expression>';

所需的结果是(空格无关紧要):

(
  [122F277B-A241-7944-BC38-3BB5E8B213AF] +
  [7DD46849-2193-EB41-8BAB-CE0C45255249] +
  (
    (
      [122F277B-A241-7944-BC38-3BB5E8B213AF] /
      [27F3156D-FDA7-1E44-B545-7F27A48D9838]
    ) *
    [ADFCEF34-9877-DE4E-8A00-13576437D82B] *
    12
  ) +
  (
    [ADFCEF34-9877-DE4E-8A00-13576437D82B] -
    [75896474-C197-1C44-8EAA-8FE9D0AB2663]
  ) +
  [27F3156D-FDA7-1E44-B545-7F27A48D9838]
)

是否有人掌握 SQL Server 2008 (R2) 中的 XQuery 足以执行此转换?

【问题讨论】:

  • 如果没有一般递归,您将无法处理具有未定义 deph 的树。这就是为什么这个 XQuery 引擎有自己的标签...
  • @Alejandro,抱歉没有看到这个标签,感谢编辑。

标签: sql-server xml sql-server-2008 recursive-query xquery-sql


【解决方案1】:

不漂亮,但它似乎工作。递归 UDF。

create function GetExpression(@expr xml) returns varchar(max)
as
begin
  declare @max int
  declare @i int = 1
  declare @nodetype varchar(50)
  declare @aggregator char(1)
  declare @res varchar(max) = '('
  declare @value varchar(36)
  declare @SubExpr xml

  select @max=count(*)
  from @expr.nodes('/expression/*') as n(e)

  select @aggregator = n.e.value('@aggregator', 'char(1)')
  from @expr.nodes('expression') as n(e)

  while @i <= @max
  begin
    select
      @nodetype = x.value('local-name(.)[1]', 'varchar(36)'),
      @value = x.value('.', 'varchar(36)'),
      @SubExpr = x.query('.')
    from @expr.nodes('/expression/*[position()=sql:variable("@i")]') e(x)

    if @nodetype = 'indicator'
      set @res = @res + '[' + @value + ']'
    else
    if @nodetype = 'expression'
      set @res = @res + dbo.GetExpression(@SubExpr)
    else  
    if @nodetype = 'value'
      set @res = @res + @value

    if @i < @max
      set @res = @res + @aggregator

    set @i = @i + 1        
  end

  set @res = @res + ')'

 return @res
end

【讨论】:

  • 非常感谢 - 确实不是很漂亮,但我想漂亮的变体由于 SQL Server XQuery 的限制而无法工作......;)我会用我的测试用例来试试(通过阅读代码,我认为它会正常工作)。
【解决方案2】:

Mikael,你让我走上了正轨,这是我的最终解决方案:

CREATE FUNCTION dbo.GetExpression (@expr xml)
RETURNS varchar(max)
AS 
BEGIN
  RETURN STUFF(
    (   SELECT a.x.value('.', 'char'), CASE 
            WHEN v.x.exist('self::expression')=1 THEN '('+dbo.GetExpression(v.x.query('.'))+')' 
            WHEN v.x.exist('self::indicator')=1 THEN '['+REPLACE(v.x.value('.', 'varchar(35)'), '-', '')+']' 
            ELSE v.x.value('.', 'varchar(20)') 
        END
        FROM @expr.nodes('expression/@aggregator') a(x)
        CROSS APPLY @expr.nodes('expression/*') v(x) 
        FOR XML PATH('')
    ), 
    1, 1, '');
END

【讨论】:

  • 好多了:)。递归 CTE 也应该是可能的,但我认为您必须使用旧的 openxml 并使用 @mp:id@mp:parentid 来构建层次结构。不知道在使用XML数据类型的时候有没有等价的功能。
  • 是的,我也想过用 CTE 做一个,它总是将没有嵌套表达式的表达式转换为文本表示,直到其中不再有任何标签,但它也没有真正起作用.我认为 UDF 是一个很好的折衷方案(甚至可能是更有效的折衷方案)。
猜你喜欢
  • 2012-03-28
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
相关资源
最近更新 更多