【问题标题】:Recursively concat columns in sql在sql中递归连接列
【发布时间】:2016-01-14 22:02:48
【问题描述】:

我有一个包含如下值的表

╔═══╦════╦════╦══════╦══════╗
║ b ║ l1 ║ l2 ║  l3  ║  l4  ║
╠═══╬════╬════╬══════╬══════╣
║ a ║ b1 ║ c1 ║  d1  ║  e1  ║
║ d ║ x1 ║ y1 ║ null ║ null ║
╚═══╩════╩════╩══════╩══════╝

输出应该是:

╔═══════════╗
║ ab1c1d1e1 ║
║ ab1c1d1   ║
║ ab1c1     ║
║ ab1       ║
║ dx1y1     ║
║ dx1       ║
╚═══════════╝

有可能吗?我在这里看到了一个模式,但能够弄清楚如何去做。 P.S: ROLLUP 不能使用,因为服务器不支持。

【问题讨论】:

    标签: mysql sql sql-server concatenation recursive-query


    【解决方案1】:

    使用UNION ALL

    SELECT * FROM(
        SELECT  b + l1 + l2 + l3 + l4 FROM tbl UNION ALL
        SELECT  b + l1 + l2 + l3 FROM tbl UNION ALL
        SELECT  b + l1 + l2 FROM tbl UNION ALL
        SELECT  b + l1 FROM tbl
    ) AS t(a)
    WHERE a IS NOT NULL
    

    执行计划:


    这是UNPIVOT 的另一种方式,它只会扫描表一次:

    SELECT x.a
    FROM tbl t
    CROSS APPLY(VALUES
        (b + l1 + l2 + l3 + l4),
        (b + l1 + l2 + l3),
        (b + l1 + l2),
        (b + l1)
    ) AS x(a)
    WHERE a IS NOT NULL
    

    执行计划:

    【讨论】:

    • 出于好奇,有没有一种方法可以在不将列和行硬编码为SELECT 的情况下执行此操作(顺便说一下,我为这个问题加注了星标)。
    • 每次选择完成时都会扫描表。而且因为它后面有一个xml。它会很贵。我需要在 3 到 5 毫秒内获得最大输出。
    • @TimBiegeleisen,可能是动态 SQL。
    • @TejalKarande,我认为没有更快的方法。另一种解决方案将涉及必须UNPIVOT 每行然后JOIN 我相信这会更贵。我把它留给你测试。
    • 好吧,展示本身需要时间。不过,我相信CROSS APPLY 将是最快的方法
    【解决方案2】:

    编辑:这个增强的解决方案将数据作为裸字符串提供。如果这很快 - 我不知道。请告诉我...

    DECLARE @tbl TABLE( b varchar(100),l1 varchar(100),l2 varchar(100),l3 varchar(100), l4 varchar(100));
    INSERT INTO @tbl VALUES
     ('a','b1','c1','d1','e1')
    ,('d','x1','y1',null,null);
    
    
    SELECT u.v.value('.','varchar(max)')
    FROM
    (
        SELECT   '' + b + l1 + l2 + l3 + l4 + ';' AS x
                ,'' + b + l1 + l2 + l3 + ';'      AS x
                ,'' + b + l1 + l2 + ';'           AS x
                ,'' + b + l1 + ';'                AS x
        FROM @tbl
        FOR XML PATH(''),TYPE
    ) AS XMLList(x)
    CROSS APPLY XMLList.x.nodes('/x') AS a(b) 
    CROSS APPLY(SELECT CAST('<r>' + REPLACE(a.b.value('.','varchar(max)'),';','</r><r>') + '</r>' AS XML)) AS m(n)
    CROSS APPLY m.n.nodes('/r') AS u(v)
    WHERE LEN(u.v.value('.','varchar(max)'))> 0
    
    /* Result
    
    ab1c1d1e1
    ab1c1d1
    ab1c1
    ab1
    dx1y1
    dx1
    
    */
    

    【讨论】:

    • 嘿,如果我将 xml 插入表中,则表创建本身需要时间,所以这不是一个选项。谢谢:)
    • @TejalKarande,您在我的回答中发现了增强的变化。结果不再是 XML,但是 - 老实说 - 我不希望这比 Felix 的回答更快。我只是好奇这是否有效......
    • 嘿..当我将它与交叉应用进行比较时,您的查询速度较慢..谢谢:)
    猜你喜欢
    • 1970-01-01
    • 2013-07-31
    • 2017-05-28
    • 2013-12-06
    • 2015-02-02
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多