根据序列号连接行

时间:2018-10-23 05:37:22

标签: sql-server tsql

我有一个表格,如下所示:

DECLARE @T AS TABLE
(
    SeqNumber INT, 
    Note VARCHAR(MAX),
    UserID INT
)

INSERT INTO @T (SeqNumber, Note, UserID) VALUES
(1, 'A', 20),
(2, 'B', 20),
(1, 'F', 20),
(2, 'K', 20),
(3, 'M', 20),
(1, 'X', 20),
(1, 'Y', 20)

我希望创建如下结果:

UserID   ResultNotes
20       'AB'
20       'FKM'
20       'X'
20       'Y'

您知道如何在MS SQL Server(T-SQL)中实现吗?它可以在MS SQL Server 2016或更早版本中。

谢谢

3 个答案:

答案 0 :(得分:3)

那是一开始的评论,但我决定为此准备一个答案...

您知道吗,没有隐式排序顺序?当SeqNumber为1时,您似乎想启动一个新组。因此-在您的样本中-A和B是第一组,FKM是第二组,依此类推。但是,如果没有其他排序顺序或任何类型的分组键,则该集合将不会被排序!。这可能会以任意顺序返回...

这是使用STRING_AGG (needs v2017+)进行分组的方法:

DECLARE @T AS TABLE
(
    GroupKey INT,
    SeqNumber INT, 
    Note VARCHAR(MAX),
    UserID INT
)

INSERT INTO @T (GroupKey,SeqNumber, Note, UserID) VALUES
(1, 1, 'A', 20),
(1, 2, 'B', 20),
(2, 1, 'F', 20),
(2, 2, 'K', 20),
(2, 3, 'M', 20),
(3, 1, 'X', 20),
(4, 1, 'Y', 20);

SELECT t.UserID
      ,STRING_AGG(Note,'') WITHIN GROUP(ORDER BY t.SeqNumber) AS ResultNotes
FROM @T t
GROUP BY t.GroupKey,t.UserID;

这适用于2017年之前的版本:

SELECT t.UserID
     ,(
        SELECT t2.Note AS [*]
        FROM @T t2
        WHERE t.GroupKey=t2.GroupKey
          AND t.UserID=t2.UserID
        ORDER BY t2.SeqNumber
        FOR XML PATH(''),TYPE
      ).value('.','nvarchar(max)') AS ResultNotes
FROM @T t
GROUP BY t.GroupKey,t.UserID

更新:按修复顺序创建GroupingKey

如果排序顺序是固定的(在我的情况下为IDENTITY-ID),请参见以下代码如何创建分组密钥:

DECLARE @T AS TABLE
(
    ID INT IDENTITY,
    SeqNumber INT, 
    Note VARCHAR(MAX),
    UserID INT
)

INSERT INTO @T (SeqNumber, Note, UserID) VALUES
(1, 'A', 20),
(2, 'B', 20),
(1, 'F', 20),
(2, 'K', 20),
(3, 'M', 20),
(1, 'X', 20),
(1, 'Y', 20);

WITH Get1s AS --set a group key per "1"
(
    SELECT t.ID,ROW_NUMBER() OVER(ORDER BY ID) AS GroupKey
    FROM @T t
    WHERE t.SeqNumber=1
)
SELECT *
      ,(SELECT MAX(x.GroupKey) FROM Get1s x WHERE t.ID>=x.ID) AS GroupKey
FROM @T t;

下一步,您可以按上述方法使用此分组键...

答案 1 :(得分:0)

DECLARE @T AS TABLE
(
    SeqNumber INT, 
    Note VARCHAR(MAX),
    UserID INT
)

INSERT INTO @T (SeqNumber, Note, UserID) VALUES
(1, 'A', 20),
(2, 'B', 20),
(1, 'F', 20),
(2, 'K', 20),
(3, 'M', 20),
(1, 'X', 20),
(1, 'Y', 20)

drop table if exists #d

select  ROW_NUMBER() OVER(ORDER BY userid ASC) AS Row#, case when SeqNumber = 1 then 1 else 0 end as t , SeqNumber,note,userid into #d from @t 
select userid,string_agg(Note,'') from
(

select isnull(t + (select sum(t) from #d as g where g.row# < #d.row#),1) as s , SeqNumber,note,userid from #d
) j
 group by s,userid

答案 2 :(得分:0)

您可以尝试以下操作:

如果seqNum以1开头并以1递增,则此解决方案将起作用

    DECLARE @T AS TABLE
    (
        SeqNumber INT, 
        Note VARCHAR(MAX),
        UserID INT
    )

    INSERT INTO @T (SeqNumber, Note, UserID) VALUES
    (1, 'A', 20),
    (2, 'B', 20),
    (3, 'C', 20),
    (4, 'D', 20),
    (5, 'E', 20),
    (1, 'F', 20),
    (2, 'K', 20),
    (3, 'M', 20),
    (1, 'X', 20),
    (1, 'Y', 20)


    ;with cte1 as
    (
        select *,
        CASE WHEN LEAD(SeqNumber) OVER(partition by userId order by (select null))>SeqNumber THEN  LEAD(SeqNumber) OVER(order by (select null))
            ELSE NULL END as leadingSeq,
        ROW_NUMBER()over(partition by userId order by (select null)) as rowNum
         from @T
    )

    ,

    cte2
    As
    (

        select c1.SeqNumber,c1.UserId,ISNULL(c1.Note,'')+ISNULL(c2.Note,'') as Note 
        ,c2.leadingSeq,c2.rowNum    
        from cte1 c1
        LEFT JOIN cte1 c2 on c1.leadingSeq=c2.SeqNumber and c2.rowNum=c1.rowNum+1 and c1.SeqNumber=1 
        Where c1.SeqNumber=1 
        or c2.Note IS NOT NULL

        Union All

        Select c1.SeqNumber,c1.UserId,ISNULL(c1.Note,'')+ISNULL(c2.Note,'') as SecNote 
        ,c2.leadingSeq,c2.rowNum     
        from cte2 c1
        JOIN cte1 c2 on c1.leadingSeq=c2.SeqNumber and c2.rowNum=c1.rowNum+1



    )

    select UserId,Note 
    from cte2 
    where leadingSeq is null