1

У меня есть XML-данные, представляющие собой математическое дерево выражений, и вы хотите преобразовать это в формулу с плоской математикой. Звучит просто, но ограничения XQuery в SQL Server в настоящее время мешают мне добиться успеха (нет рекурсивных функций, проблем с «неоднородными» результатами и т. Д.).Преобразование дерева XML в плоский текст с использованием SQL Server 2008 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] 
) 

Является ли кто-то мастером XQuery в SQL Server 2008 (R2) достаточно хорошо, чтобы выполнить это преобразование?

+0

Вы не можете обработать дерево с неопределенным deph без общей рекурсии. Вот почему этот движок XQuery имеет свой собственный тег ... –

+0

@Alejandro, извините, не поймал этот тег, спасибо за редактирование. – Lucero

ответ

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 
+0

Большое спасибо - не очень-то красиво, но я думаю, что красивые варианты не работают из-за ограничений SQL Server XQuery ...;) Я попробую с моими тестовыми примерами (от чтения кода я думаю, что он будет работать нормально). – Lucero

1

Микаэль, ты меня на верном пути, вот мое окончательное решение:

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 
+0

Много приятнее :). Это также должно быть возможно с рекурсивным CTE, но я думаю, что вы должны использовать старый 'openxml' и использовать' @mp: id' и '@mp: parentid' для построения иерархии. Я не знаю, есть ли какие-либо эквивалентные функции при использовании типа данных XML. –

+0

Да, я также хотел бы сделать одно с CTE, которое всегда преобразует выражения без вложенных выражений в текстовое представление, пока в нем больше нет тегов, но на самом деле это тоже не работает. Я думаю, что UDF - хороший компромисс (и, возможно, даже более эффективный). – Lucero