使用 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
子句中。
我的 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
子句中。