使用 sql stuff 函数折叠行

collapse rows using sql stuff function

我的 sql 数据库中有三个表,我正在尝试使用 3 个表中的填充函数折叠 sql 服务器中的行

我曾尝试使用内部连接并使用外部连接,但没有得到预期的结果 我的代码在下面:

select T1.Id, 
       T1.Name,
       T2.Id,
       T2.T1_Id,
       T2.Name,
       stuff(
       (
           select ','+T3.Name 
           from Test3 T3 
           where T3.Id=T2.T3_Id for xml path('')
       ),1,1,'') as Test5
from Test1 T1,Test2 T2 
where T1.Id=T2.T1_Id

我得到了这样的结果

Id  Name    Id  T1_Id   Name    Test5
----------------------------------------------
1   Test1   1    1      ASD      BAAN
1   Test1   2    1      ASD      KAAL

预计

Id  Name    Id  T1_Id   Name    Test5
1   Test1   1     1      ASD  BAAN,KAAL

我的表脚本和示例数据是,我正在使用 sql 服务器 2014

   CREATE TABLE [dbo].[Test1](
        [Id] [int] NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]
    GO

   INSERT INTO [dbo].[Test1]
           ([Id]
           ,[Name])
     VALUES
           (1
           ,'Test1')
           GO

    CREATE TABLE [dbo].[Test2](
        [Id] [int] NOT NULL,
        [T1_Id] [int] NOT NULL,
        [T3_Id] [int] NOT NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]


    INSERT INTO [dbo].[Test2]
           ([Id]
           ,[T1_Id]
           ,[T3_Id]
           ,[Name])
     VALUES
           (1
           ,1
           ,1
           ,'ASD'),(2,1,2,'ASD')
           GO


    CREATE TABLE [dbo].[Test3](
        [Id] [int] NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]

    GO

   INSERT INTO [dbo].[Test3]
           ([Id]
           ,[Name])
     VALUES
           (1
           ,'KAAL'),(2,'BAAL')
           Go

尝试以下脚本。

注意:您必须避免使用 Test2 和 Test3 中的 ID,否则您不能对 STUFF 应用 GROUP BY,因为它们在 ID 列中具有不同的值。

SELECT *,
STUFF
(
    (
        SELECT ',' + Name 
        FROM
        (
            SELECT T3.Name
            FROM Test1 T1 
            INNER JOIN Test2 T2 ON T1.Id = T2.T1_Id
            INNER JOIN Test3 T3 ON T2.T3_Id = T3.ID
        ) B
        FOR XML PATH ('')
    ), 1, 1, ''
) Test5
FROM
(
    SELECT T1.Id,T1.Name T1_Name,T2.Name T2_Name
    FROM Test1 T1 
    INNER JOIN Test2 T2 ON T1.Id = T2.T1_Id
)A  
GROUP BY A.Id,A.T1_Name,A.T2_Name

SQL 语言中没有 "COLLAPSE"。您发布的内容显示分组和聚合。对于字符串,唯一有意义的聚合是 MIN、MAX 和字符串连接。

SQL Server 2017 通过 STRING_AGG 函数提供字符串连接。其他数据库产品使用不同的名称,例如 GROUP_CONCAT。在早期的 SQL 服务器版本中,使用了多种技术来实现相同的目的。你发的是XML技巧

查询没有 GROUP BY 子句,这就是返回多行的原因。

在 SQL Server 2017 中,查询将如下所示:

select T1.Id, 
       T1.Name,
       MIN(T2.Id) as T2_ID,
       MIN(T2.T1_Id) as T1_ID,
       MIN(T2.Name) as T2_Name,
       STRING_AGG(T3.Name,', ') as Test5
from @Test1 T1 
    inner join @Test2 T2 on T1.Id=T2.T1_Id
    inner join @Test3 T3 on T3.Id=T2.T3_Id 
GROUP BY T1.ID,T1.Name

在早期版本中做同样的事情比较棘手。 FOR XML 查询只能与出现在 GROUP BY 子句中的列关联:

select T1.Id, 
       T1.Name,
       MIN(T2.Id),
       MIN(T2.T1_Id),
       MIN(T2.Name),
       stuff(
       (
           SELECT ','+T3.Name 
           FROM @Test3 T3 
              inner join @Test2 TT2 on T3.Id=TT2.T3_Id
           WHERE TT2.T1_ID=T1.Id 
           FOR XML PATH('')
       ),1,1,'') as Test5
from @Test1 T1 
    inner join @Test2 T2 on T1.Id=T2.T1_Id
GROUP BY T1.ID,T1.Name

将整个 STUFF(... FOR XML).. 部分视为一个函数,它将 WHERE 子句中的分组列之一作为参数,搜索一些表并连接字符串结果。

这意味着关联子句

WHERE TT2.T1_ID=T1.Id 

只能引用外部查询中的分组列。我们需要在那里加入 T2,因为我们无法直接从 T3 T1.ID

如果你不关心 T2 列,你可以去掉外部查询中的 JOIN :

select T1.Id, 
       T1.Name,
       stuff(
       (
           select ','+T3.Name 
           from @Test3 T3 
              inner join @Test2 TT2 on T3.Id=TT2.T3_Id
              where TT2.T1_ID=T1.Id for xml path('')
       ),1,1,'') as Test5
from @Test1 T1 
GROUP BY T1.ID,T1.Name

试试这个

;WITH CTE AS 
(
SELECT T1.Id AS T1_Id,
       T1.Name AS T1_Name,
       T2.Id  AS  T2_Id,
       T2.T1_Id AS T2_T1_Id,
       T2.Name AS T2_Name, 
       T3.Name AS Test5
FROM [Test2] t2
    INNER JOIN [Test1] t1
ON t1.Id = t2.T1_Id
    INNER JOIN [Test3] t3
ON t3.id = t2.T3_Id
)
SELECT  T1_Id,  
        T1_Name,            
        T2_T1_Id,   
        T2_Name,    
        STUFF((SELECT  ', '+Test5 
                FROM CTE i WHERE i.T1_Id = o.T1_Id 
                FOR XML PATH ('')),1,1,'') AS Test5
FROM CTE o
GROUP BY T1_Id, 
        T1_Name,            
        T2_T1_Id,   
        T2_Name

如果你不关心 Test2.Name 你可以得到如下结果:

WITH tblMain as (SELECT T1.Id,T1.Name, T3.Name T3Name
                 FROM Test2 T2 LEFT OUTER JOIN
                 Test1 T1 on T2.T1_Id = T1.Id LEFT OUTER JOIN
                 Test3 T3 on T2.T3_Id = T3.Id)

SELECT Id, Name, 
    STUFF((SELECT ', ' + T3Name
           FROM tblMain b 
           WHERE b.Id = a.Id AND b.Name = a.Name 
           FOR XML PATH('')), 1, 2, '') as Test5
FROM tblMain a
GROUP BY Id, Name

否则,如果您想要 Test2 中的其他列,您也应该将它们添加到 GROUP BY 子句中。