SQLServer将三个不同的列连接到一个逗号中,逗号之间没有重复的值

zrfyljdw  于 2021-07-24  发布在  Java
关注(0)|答案(3)|浏览(304)

下表是我在sql server中遇到的问题的简化:

ID  COLUMN_A    COLUMN_B    COLUMN_C
-------------------------------------
1    A            B           C
1    A            B           D
1    B            C           D

我想得到一个列由逗号连接而没有重复值的组。我尝试使用字符串\u agg(),但它返回:

ID  COLUMN_A    COLUMN_B    COLUMN_C
-------------------------------------
1    A, A, B    B, B, C     C, D, D

这是我所做的查询:

SELECT ID, STRING_AGG(COLUMN_A, ', ') AS COL_A, STRING_AGG(COLUMN_B, ', ') AS COL_B,
 STRING_AGG(COLUMN_C, ', ') AS COL_C   
FROM MYTABLE
GROUP BY ID;

我想要下一个结果:

ID  COLUMN_A    COLUMN_B    COLUMN_C
-------------------------------------
1    A, B        B, C        C, D

谢谢您!

mkh04yzy

mkh04yzy1#

不使用 window functions . 这个 union 可能会放慢速度,但试一下,看看你是否能忍受这种表现。

with 
 cte1 (id, col, indicator) as 

  (select id, column_a, 'col1' from t union
   select id, column_b, 'col2' from t union
   select id, column_c, 'col3' from t),

 cte2 (id, indicator, agg) as

  (select id, indicator, string_agg(col,',') 
   from cte1
   group by id, indicator)

 select id,
        max(case when indicator='col1' then agg end) as column_a,
        max(case when indicator='col2' then agg end) as column_b,
        max(case when indicator='col3' then agg end) as column_c
 from cte2
 group by id;
0yg35tkg

0yg35tkg2#

下面是基于xml和xquery的解决方案。
sql语句

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT, COLUMN_A CHAR(1), COLUMN_B CHAR(1), COLUMN_C CHAR(1));
INSERT INTO @tbl (ID, COLUMN_A, COLUMN_B, COLUMN_C)
VALUES
(1,'A','B','C'),
(1,'A','B','D'),
(1,'B','C','D');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = ',';

;WITH rs AS
(
    SELECT ID
        , CAST('<root><r><![CDATA[' + 
            REPLACE(STRING_AGG(COLUMN_A, ','), @separator, ']]></r><r><![CDATA[') +
            ']]></r></root>' AS XML) AS COL_A
        , CAST('<root><r><![CDATA[' + 
            REPLACE(STRING_AGG(COLUMN_B, ','), @separator, ']]></r><r><![CDATA[') +
            ']]></r></root>' AS XML) AS COL_B
        , CAST('<root><r><![CDATA[' + 
            REPLACE(STRING_AGG(COLUMN_c, ','), @separator, ']]></r><r><![CDATA[') +
            ']]></r></root>' AS XML) AS COL_C

    FROM @tbl
    GROUP BY ID
)
SELECT rs.ID
    , COL_A.query('for $i in distinct-values(/root/r/text())
       return if ($i eq (distinct-values(/root/r/text())[last()])[1]) then $i
             else concat($i, sql:variable("@separator"))
    ').value('.', 'NVARCHAR(MAX)') AS COL_A
    , COL_B.query('for $i in distinct-values(/root/r/text())
       return if ($i eq (distinct-values(/root/r/text())[last()])[1]) then $i
             else concat($i, sql:variable("@separator"))
    ').value('.', 'NVARCHAR(MAX)') AS COL_B
    , COL_C.query('for $i in distinct-values(/root/r/text())
       return if ($i eq (distinct-values(/root/r/text())[last()])[1]) then $i
             else concat($i, sql:variable("@separator"))
    ').value('.', 'NVARCHAR(MAX)') AS COL_C
FROM rs;

输出

+----+-------+-------+-------+
| ID | COL_A | COL_B | COL_C |
+----+-------+-------+-------+
|  1 | A, B  | B, C  | C, D  |
+----+-------+-------+-------+
wecizke3

wecizke33#

不幸的是, string_agg(distinct) 还不起作用。但是你可以做一些更复杂的事情:

SELECT ID,
      STRING_AGG(CASE WHEN seqnum_a = 1 THEN COLUMN_A, END ', ') AS COLUMN_A,
      STRING_AGG(CASE WHEN seqnum_b = 1 THEN COLUMN_B, END ', ') AS COLUMN_B,
      STRING_AGG(CASE WHEN seqnum_c = 1 THEN COLUMN_C, END ', ') AS COLUMN_C
FROM (SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY ID, COLUMN_A ORDER BY ID) as seqnum_a,
             ROW_NUMBER() OVER (PARTITION BY ID, COLUMN_B ORDER BY ID) as seqnum_b,
             ROW_NUMBER() OVER (PARTITION BY ID, COLUMN_C ORDER BY ID) as seqnum_c
      FROM MYTABLE t
     ) t
GROUP BY ID;

所以,尽管 STRING_AGG() 不会删除重复项,但会忽略 NULL 价值观。

相关问题