【问题标题】:How can I convert a hierarchical table to xml in SQL如何在 SQL 中将分层表转换为 xml
【发布时间】:2020-07-24 10:00:22
【问题描述】:

我需要使用 t-SQL 将查询结果转换为 xml 片段。 我的表/查询如下所示:

[Col1]  [Col2]      [Col3]      qty
A       group1      mod1        5
A       group1      mod2        7
A       group1      NULL        12
A       group2      mod3        8
A       group2      mod4        5
A       group2      NULL        13
A       NULL        NULL        25

所需的 XML 输出应如下所示

<A qty="25">
    <group1 qty="12">
        <mod1 qty="5"/>
        <mod2 qty="7"/>
    </group1>
    <group2 qty="13">
        <mod3 qty="8"/>
        <mod4 qty="5"/>
    </group2>
</A>

有没有一种方法可以在不旋转表和使用动态 sql 的情况下实现这一点,这会非常混乱

【问题讨论】:

  • 尝试xml路径。谷歌 xml sql 服务器
  • 这和你的问题有点相似。 stackoverflow.com/a/36768199/1068246
  • @MuzafferGALATA。感谢您的链接。这解决了嵌套问题。我也在尝试找到一种将列值作为元素名称的方法。但这似乎不可能
  • @MuzafferGALATA 这与您链接到的问题相去甚远,因为非常重要的一点:该链接提供了一个解决方案,其中列的名称是节点的名称(这是 FOR XML有效),但在这种情况下,OP 希望以 VALUE 成为节点名称的方式构建 XML。这是一个巨大的不同。乍一看,这里似乎需要一个支点/反支点或循环。
  • 你是对的@RonenAriely。

标签: sql-server xml tsql


【解决方案1】:

您好,请确认这可以解决您的需求。我们接下来可以努力提高性能。拥有正确的索引很重要

DDL+DML

DROP TABLE IF EXISTS T
GO

CREATE TABLE T(
    [Col1] VARCHAR(10),
    [Col2] VARCHAR(10),
    [Col3] VARCHAR(10),
    qty VARCHAR(10)
)
GO

INSERT T([Col1],[Col2],[Col3],qty) VALUES
('A', 'group1', 'mod1', 5 ),
('A', 'group1', 'mod2', 7 ),
('A', 'group1',  NULL , 12),
('A', 'group2', 'mod3', 8 ),
('A', 'group2', 'mod4', 5 ),
('A', 'group2', NULL  , 13),
('A',  NULL   , NULL  , 25)
GO

SELECT [Col1],[Col2],[Col3],qty 
FROM T
GO

解决方案

;with l1 as (
    SELECT COL1, qty
    FROM T
    where col2 is null and col3 is null
)
, l2 as (
    SELECT Col1, COL2, qty
    FROM T
    where col2 is not null and col3 is null
)
, l3 as (
    SELECT Col1, COL2, COL3, qty
    FROM T
    where col2 is not null and col3 is not null
)
, a1 as (
    SELECT COL2, X = STRING_AGG('<' + COL3 + ' qty="' + qty + '"/>' ,'') 
    FROM l3
    GROUP BY Col2
)
, a2 as (
    select X = '<' + a1.Col2 + ' qty="' + l2.qty + '">' + a1.X + '</' + a1.Col2 + '>', l2.Col1
    from a1 
    LEFT JOIN l2 on a1.Col2 = l2.Col2
)
select CONVERT(XML,
    '<' + l1.Col1 + ' qty="' + l1.qty + '">' + STRING_AGG(a2.X,'') + '</' + l1.Col1 + '>' 
)
from a2
left join l1 on l1.Col1 = a2.Col1
group by l1.Col1, l1.qty
GO

注意!最后,我将值转换为 XML 以检查结果是否合适。在生产中没有理由执行此步骤,这会耗费资源。

【讨论】:

    【解决方案2】:

    @RonenAriely 你的解决方案启发了我。我稍微修改了一下。因为我无法运行它。 我的解决方案看起来有点脏、复杂而且不优雅,但结果是正确的。

    DECLARE @Tbl TABLE (Col1 CHAR(1), Col2 CHAR(6), Col3 CHAR(4), qty INT);
    INSERT INTO @Tbl
    VALUES ('A', 'group1', 'mod1', 5),
           ('A', 'group1', 'mod2', 7),
           ('A', 'group1', '', 12),
           ('A', 'group2', 'mod3', 8),
           ('A', 'group2', 'mod4', 5),
           ('A', 'group2', '', 13),
           ('A', '', '', 25);
    
    
    
    WITH l1
      AS (SELECT Col1,
                 Col2,
                 SUM(qty) AS qty FROM @Tbl WHERE Col2 <> ''
             AND Col3 <> ''
             AND Col2 <> ''
           GROUP BY Col1,
                    Col2),
      l2
      AS (SELECT Col2,
                 SUM(qty) AS qty FROM @Tbl WHERE Col3 <> ''
           GROUP BY Col2),
      l3
      AS (SELECT Col2,
                 Col3,
                 SUM(qty) AS qty FROM @Tbl
           GROUP BY Col2,
                    Col3),
      a1
      AS (SELECT Col2,
                 '<' + Col3 + ' qty="' + CAST(qty AS VARCHAR(10)) + '"/>' AS c FROM l3 WHERE Col3 <> ''),
    
      a2
      AS (SELECT l1.Col1,
                 '<' + aa.Col2 + ' qty="' + CAST(l2.qty AS VARCHAR(10)) + '">' +
    
                 STUFF((SELECT DISTINCT '' + c FROM a1 WHERE Col2 = aa.Col2
                     FOR XML PATH ('')), 1, 0, '')
                 + '</' + aa.Col2 + '>' AS c FROM a1 aa
            LEFT JOIN l1
              ON l1.Col2 = aa.Col2
            LEFT JOIN l2
              ON aa.Col2 = l2.Col2
           GROUP BY l1.Col1,
                    aa.Col2,
                    l2.qty)
    
    SELECT REPLACE(REPLACE(REPLACE('<' + aa.Col1 + ' qty="' + CAST(l1.qty AS VARCHAR(10)) + '">'
    
        + STUFF((SELECT DISTINCT '' + c FROM a2 WHERE Col1 = aa.Col1
            FOR XML PATH ('')), 1, 0, '')
        + '</' + aa.Col1 + '>', '&amp;', '&'), '&lt;', '<'), '&gt;', '>') AS c
    
      FROM a2 aa
      LEFT OUTER JOIN (SELECT Col1, SUM(qty) AS qty FROM l1 GROUP BY Col1) l1
        ON l1.Col1 = aa.Col1
    GROUP BY aa.Col1,
             l1.qty;
    

    【讨论】:

      【解决方案3】:

      回复我自己的帖子。

      感谢大家的帮助。最后,我使用动态 SQL 实现了一个解决方案,因为我有很多块要处理,我需要一个通用的解决方案。我的最终代码如下所示:

      DECLARE @SQL nvarchar(4000)
      SELECT @SQL = 'SELECT ' + 
        STUFF((SELECT ',' + qty + ' as ''' 
               + Col1 
               + ISNULL('/'+ Col2,'') 
               + ISNULL('/'+ Col3,'') 
               + '/@qty''' FROM T ORDER BY Col1, Col2, Col3 FOR XML PATH('')) 
            ,1,1,'') + ' FOR XML PATH('''')'
      EXEC(@SQL)
      

      不幸的是,它不能作为一个函数来实现(这是我最初的目标),因为它需要一个物理表并且由于字符串大小的限制。这也不适用于具有许多子级别的大型数据集。我将不得不忍受它!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-21
        • 1970-01-01
        • 2012-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多