2016-09-06 3 views
3

Я предопределю это, сказав, что я ненавижу XML, ужасные вещи для работы, но необходимо иногда.Измельчение XML из планов выполнения

Моя текущая проблема заключается в том, что я пытаюсь взять XML из плана выполнения (предоставленного пользователем, так что может быть любого размера) и уничтожить его в таблицу для дальнейшей манипуляции. На данный момент у меня нет двух вариантов;

  1. Я мог бы вычислить максимальное количество узлов, доступных для плана выполнения (я подозреваю, что это будет много) и создать весь скрипт, который может быть использован для любого ввода XML. Это было бы одно время, и это не проблема.
  2. Альтернативой будет динамическое определение количества узлов и создание результата в соответствии с требованиями.

Неужели кто-то делал подобное упражнение в прошлом? Все тестовые запросы, которые я нашел, уже знают поля вывода.

ответ

4

очень прямой путь может быть это (в то время как @x ваш XML-выполнение плана):

DECLARE @x XML= 
N'<root> 
    <ElementE1 AttributA1="A1-text belongs to E1[1]" OneMore="xyz">E1-Text 2</ElementE1> 
    <ElementE1 AttributA1="A1-text belongs to E1[2]">E1-Text 2</ElementE1> 
    <ElementParent> 
     <subElement test="sub"/> 
     Free text 
    </ElementParent> 
    </root>'; 

DECLARE @idoc INT; 
EXEC sp_xml_preparedocument @idoc OUTPUT, @x; 
SELECT * FROM OPENXML (@idoc, '*'); 
EXEC sp_xml_removedocument @idoc; 

Результат (не все столбцы)

+----+----------+----------+--------------+------+--------------------------+ 
| id | parentid | nodetype | localname | prev | text      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 0 | NULL  | 1  | root   | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 2 | 0  | 1  | ElementE1 | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 3 | 2  | 2  | AttributA1 | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 13 | 3  | 3  | #text  | NULL | A1-text belongs to E1[1] | 
+----+----------+----------+--------------+------+--------------------------+ 
| 4 | 2  | 2  | OneMore  | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 14 | 4  | 3  | #text  | NULL | xyz      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 5 | 2  | 3  | #text  | NULL | E1-Text 2    | 
+----+----------+----------+--------------+------+--------------------------+ 
| 6 | 0  | 1  | ElementE1 | 2 | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 7 | 6  | 2  | AttributA1 | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 15 | 7  | 3  | #text  | NULL | A1-text belongs to E1[2] | 
+----+----------+----------+--------------+------+--------------------------+ 
| 8 | 6  | 3  | #text  | NULL | E1-Text 2    | 
+----+----------+----------+--------------+------+--------------------------+ 
| 9 | 0  | 1  | ElementParent| 6 | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 10 | 9  | 1  | subElement | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 11 | 10  | 2  | test   | NULL | NULL      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 16 | 11  | 3  | #text  | NULL | sub      | 
+----+----------+----------+--------------+------+--------------------------+ 
| 12 | 9  | 3  | #text  | 10 | Free text    | 
+----+----------+----------+--------------+------+--------------------------+ 

id показывает, что алгоритм шириной первый, нет id=1 (почему ev er), а nodetype позволяет различать элементы, атрибуты и (плавающий) текст. Столбец prev указывает на родственников в цепочке. Недостающие колонны связаны с пространствами имен ...

Подход с FROM OPENXML устарел, но это один из тех редких случаях, это может все еще быть очень полезным ...

Вы получите список с идентификаторами и ParentIDs вы можете запросить с рекурсивным CTE ... Это зависит от того, что вы хотите сделать с этим потом ...

3

Скрипт для уничтожения XML до элементов и атрибутов с их путями и родителями. От http://beyondrelational.com/modules/2/blogs/28/posts/10495/xquery-lab-58-select-from-xml.aspx

CREATE FUNCTION [dbo].[XMLTable]( 
    @x XML 
) 
RETURNS TABLE 
AS RETURN 
/*---------------------------------------------------------------------- 
This INLINE TVF uses a recursive CTE that processes each element and 
attribute of the XML document passed in. 
----------------------------------------------------------------------*/ 
WITH cte AS ( 
    /*------------------------------------------------------------------ 
    Anchor part of the recursive query. Retrieves the root element 
    of the XML document 
    ------------------------------------------------------------------*/ 
    SELECT 
     1 AS lvl, 
     x.value('local-name(.)','NVARCHAR(MAX)') AS Name, 
     CAST(NULL AS NVARCHAR(MAX)) AS ParentName, 
     CAST(1 AS INT) AS ParentPosition, 
     CAST(N'Element' AS NVARCHAR(20)) AS NodeType, 
     x.value('local-name(.)','NVARCHAR(MAX)') AS FullPath, 
     x.value('local-name(.)','NVARCHAR(MAX)') 
      + N'[' 
      + CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS NVARCHAR) 
      + N']' AS XPath, 
     ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS Position, 
     x.value('local-name(.)','NVARCHAR(MAX)') AS Tree, 
     x.value('text()[1]','NVARCHAR(MAX)') AS Value, 
     x.query('.') AS this,   
     x.query('*') AS t, 
     CAST(CAST(1 AS VARBINARY(4)) AS VARBINARY(MAX)) AS Sort, 
     CAST(1 AS INT) AS ID 
    FROM @x.nodes('/*') a(x) 
    UNION ALL 
    /*------------------------------------------------------------------ 
    Start recursion. Retrieve each child element of the parent node 
    ------------------------------------------------------------------*/ 
    SELECT 
     p.lvl + 1 AS lvl, 
     c.value('local-name(.)','NVARCHAR(MAX)') AS Name, 
     CAST(p.Name AS NVARCHAR(MAX)) AS ParentName, 
     CAST(p.Position AS INT) AS ParentPosition, 
     CAST(N'Element' AS NVARCHAR(20)) AS NodeType, 
     CAST( 
      p.FullPath 
      + N'/' 
      + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX) 
     ) AS FullPath, 
     CAST( 
      p.XPath 
      + N'/' 
      + c.value('local-name(.)','NVARCHAR(MAX)') 
      + N'[' 
      + CAST(ROW_NUMBER() OVER(
       PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)') 
       ORDER BY (SELECT 1)) AS NVARCHAR ) 
      + N']' AS NVARCHAR(MAX) 
     ) AS XPath, 
     ROW_NUMBER() OVER(
       PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)') 
       ORDER BY (SELECT 1)) AS Position, 
     CAST( 
      SPACE(2 * p.lvl - 1) + N'|' + REPLICATE(N'-', 1) 
      + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX) 
     ) AS Tree, 
     CAST(c.value('text()[1]','NVARCHAR(MAX)') AS NVARCHAR(MAX)) AS Value, 
     c.query('.') AS this,   
     c.query('*') AS t, 
     CAST( 
      p.Sort 
      + CAST((lvl + 1) * 1024 
      + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS VARBINARY(4) 
     ) AS VARBINARY(MAX)) AS Sort, 
     CAST( 
      (lvl + 1) * 1024 
      + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS INT 
     ) 
    FROM cte p 
    CROSS APPLY p.t.nodes('*') b(c)   
), cte2 AS ( 
    SELECT 
     lvl AS Depth, 
     Name AS NodeName, 
     ParentName, 
     ParentPosition, 
     NodeType, 
     FullPath, 
     XPath, 
     Position, 
     Tree AS TreeView, 
     Value, 
     this AS XMLData, 
     Sort, ID 
    FROM cte 
    UNION ALL 
    /*------------------------------------------------------------------ 
    Attributes do not need recursive calls. So add the attributes 
    to the query output at the end. 
    ------------------------------------------------------------------*/ 
    SELECT 
     p.lvl, 
     x.value('local-name(.)','NVARCHAR(MAX)'), 
     p.Name, 
     p.Position, 
     CAST(N'Attribute' AS NVARCHAR(20)), 
     p.FullPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'), 
     p.XPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'), 
     1, 
     SPACE(2 * p.lvl - 1) + N'|' + REPLICATE('-', 1) 
      + N'@' + x.value('local-name(.)','NVARCHAR(MAX)'), 
     x.value('.','NVARCHAR(MAX)'), 
     NULL, 
     p.Sort, 
     p.ID + 1 
    FROM cte p 
    CROSS APPLY this.nodes('/*/@*') a(x) 
) 
SELECT 
    ROW_NUMBER() OVER(ORDER BY Sort, ID) AS ID, 
    ParentName, ParentPosition,Depth, NodeName, Position, 
    NodeType, FullPath, XPath, TreeView, Value, XMLData 
FROM cte2; 
go 
SELECT * FROM dbo.XMLTable(' 
<employees> 
    <emp name="jacob"/> 
    <emp name="steve"> 
     <phone>123</phone> 
    some text      
    </emp> 
</employees> 
') 
+1

Спасибо, я дам вам все и вернусь к вам. –

+0

Я тоже попробую этот метод. – Abe

+0

Метод работает. Это сработало для меня. Благодарю. – Abe

Смежные вопросы