id Name Value
1 A 4
1 B 8
2 C 9
到
id Column
1 A:4, B:8
2 C:9
#1 楼
无需CURSOR,WHILE循环或用户定义的功能。只需要使用FOR XML和PATH进行创作即可。
[注意:此解决方案仅适用于SQL 2005和更高版本。原始问题未指定使用的版本。]
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
评论
为什么一个人锁定一个临时表?
–艾米B
08年11月7日在19:33
这是我一生中见过的最酷的SQL内容。知道大型数据集是否“快速”吗?它不会像游标那样开始爬行,不是吗?我希望更多的人会投票赞成这种疯狂。
–user12861
08年7月7日在21:27
嗯我只是讨厌它的子查询风格。联接好多了。只是不要以为我可以在此解决方案中利用它。无论如何,我很高兴看到除了我之外这里还有其他喜欢学习类似SQL知识的人。你们所有人都感到荣幸:)
–凯文·费尔柴尔德(Kevin Fairchild)
08年11月7日在22:02
一种更简洁的字符串处理方式:STUFF((SELECT','+ [Name] +':'+ CAST([Value] AS VARCHAR(MAX))FROM #YourTable WHERE(ID = Results.ID)FOR XML PATH('')),1,2,'')AS名称值
–乔纳森·赛斯(Jonathan Sayce)
2011-10-18 10:54
只是为了说明我发现的东西。即使在不区分大小写的环境中,查询的.value部分也需要小写。我猜这是因为它是XML,区分大小写
– Jaloopa
13年7月30日在15:22
#2 楼
如果是SQL Server 2017或SQL Server Vnext,SQL Azure,则可以如下使用string_agg
:select id, string_agg(concat(name, ':', [value]), ', ')
from #YourTable
group by id
评论
完美无瑕!
–argoo
19/12/3在10:02
#3 楼
评论
我在网上搜寻了寻找不对输出进行编码的最佳方法。非常感谢!这是绝对的答案-直到MS添加对此的适当支持,例如CONCAT()聚合函数。我要做的就是将其放入返回我的串联字段的外部应用程序中。我不喜欢在我的选择语句中添加嵌套选择。
–MikeTeeVee
13年3月15日在19:01
我同意,在不使用Value的情况下,如果文本是XML编码的字符,我们会遇到问题。请在我的博客中找到有关SQL Server中分组串联的方案。 blog.vcillusion.co.in/…
– vCillusion
18年6月2日在22:23
#4 楼
当我尝试将Kevin Fairchild的建议转换为包含已编码的空格和特殊XML字符(&
,<
,>
)的字符串时,遇到了两个问题。我的代码的最终版本(不能回答原始问题,但可能对某人有用)如下所示:
CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
而不是使用空格作为定界符并将所有空格替换为逗号,它只是在每个值前加上逗号和空格,然后使用
STUFF
删除前两个字符。使用TYPE指令自动处理XML编码。
#5 楼
使用Sql Server 2005及更高版本的另一种选择---- test data
declare @t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert @t select 1125439 ,'CKT','Approved'
insert @t select 1125439 ,'RENO','Approved'
insert @t select 1134691 ,'CKT','Approved'
insert @t select 1134691 ,'RENO','Approved'
insert @t select 1134691 ,'pn','Approved'
---- actual query
;with cte(outputid,combined,rn)
as
(
select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
from @t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
评论
感谢您的输入,我始终喜欢使用CTE和递归CTE解决SQL Server中的问题。这对我来说是一件好事!
– gbdavid
2015年5月29日在8:57
是否可以在带有外部Apply的查询中使用它?
–洞中有火
15年10月28日在10:01
#6 楼
从http://groupconcat.codeplex.com安装SQLCLR聚合然后您可以编写如下代码来获得所需的结果:
CREATE TABLE foo
(
id INT,
name CHAR(1),
Value CHAR(1)
);
INSERT INTO dbo.foo
(id, name, Value)
VALUES (1, 'A', '4'),
(1, 'B', '8'),
(2, 'C', '9');
SELECT id,
dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM dbo.foo
GROUP BY id;
评论
几年前使用它时,该语法比所有“ XML Path”技巧都干净得多,并且效果很好。如果选择SQL CLR函数,则强烈建议您这样做。
–AFract
16-09-13在8:24
#7 楼
八年后... Microsoft SQL Server vNext数据库引擎终于增强了Transact-SQL,以直接支持分组字符串连接。社区技术预览版1.0添加了STRING_AGG函数,而CTP 1.1添加了STRING_AGG函数的WITHIN GROUP子句。参考:https://msdn.microsoft.com/zh-cn/library/mt775028 .aspx
#8 楼
SQL Server 2005和更高版本允许您创建自己的自定义聚合函数,包括诸如级联之类的内容,请参见链接文章底部的示例。评论
不幸的是,这要求(?)使用CLR程序集..这是另一个要解决的问题:-//
–user166390
2012年7月11日在20:27
仅该示例将CLR用于实际的串联实现,但这不是必需的。您可以使串联聚合函数使用FOR XML,因此至少在以后调用它会更巧妙!
– Shiv
16-09-26在4:45
#9 楼
这只是Kevin Fairchild职位的补充(顺便说一句,非常聪明)。我会将其添加为评论,但是我还没有足够的要点:)我将这个想法用于我正在研究的视图,但是我要补充的项目包含空格。所以我稍微修改了代码以不使用空格作为定界符。
再次感谢Kevin的出色解决方法!
CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT )
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9)
SELECT [ID],
REPLACE(REPLACE(REPLACE(
(SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A
FROM #YourTable
WHERE ( ID = Results.ID )
FOR XML PATH (''))
, '</A><A>', ', ')
,'<A>','')
,'</A>','') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
#10 楼
在Oracle中,您可以使用LISTAGG聚合函数。
原始记录
name type
------------
name1 type1
name2 type2
name2 type3
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
结果在
name type
------------
name1 type1
name2 type2; type3
评论
看起来不错,但是问题不是关于Oracle的。
–user12861
13年2月7日在15:43
我明白。但是我一直在为Oracle寻找相同的东西,所以我想把它放在这里给像我这样的其他人:)
– Michal B.
13年2月8日在10:36
@MichalB。您不是错过了inner语法吗?例如:listagg(type,',')在group(按名称排序)中?
–格雷戈里
17年2月10日在21:52
@gregory:我编辑了答案。我认为我的旧解决方案过去曾经可以使用。您建议的当前表单肯定可以使用,谢谢。
– Michal B.
17-2-15在10:57
对于未来的人-您可以使用自己的答案写一个新问题,以解决诸如平台等重大差异
– Mike M
18年3月22日在11:15
#11 楼
这种问题在这里经常问到,解决方案将在很大程度上取决于基本要求:https://stackoverflow.com/search?q=sql+pivot
和
https://stackoverflow.com/search?q=sql+concatenate
通常,没有这种方法,没有SQL的唯一方法动态sql,用户定义函数或游标。
评论
不对。 cyberkiwi使用cte:s的解决方案是纯SQL,没有任何特定于供应商的黑客。
–BjörnLindqvist
13年7月25日在11:24
在提出问题时,我不会将递归CTE视为可移植性非常好,但是Oracle现在支持它们。最好的解决方案将取决于平台。对于SQL Server,很可能是FOR XML技术或客户CLR聚合。
–Cade Roux
13年7月25日在15:11
所有问题的最终答案? stackoverflow.com/search?q= [任何问题]
–刘俊臣
16 Dec 8'在11:07
#12 楼
只是为了补充Cade所说的,这通常是前端显示,因此应在此处处理。我知道有时候为文件导出或其他“仅SQL”解决方案之类的事情,用SQL编写100%的内容有时会更容易,但是大多数情况下,这种串联应该在您的显示层中进行处理。评论
分组是前端显示的东西吗?有许多有效的方案可以将分组结果集中的一列串联起来。
– MGOwen
16年8月3日,下午3:21
#13 楼
不需要游标... while循环就足够了。------------------------------
-- Setup
------------------------------
DECLARE @Source TABLE
(
id int,
Name varchar(30),
Value int
)
DECLARE @Target TABLE
(
id int,
Result varchar(max)
)
INSERT INTO @Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO @Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO @Source(id, Name, Value) SELECT 2, 'C', 9
------------------------------
-- Technique
------------------------------
INSERT INTO @Target (id)
SELECT id
FROM @Source
GROUP BY id
DECLARE @id int, @Result varchar(max)
SET @id = (SELECT MIN(id) FROM @Target)
WHILE @id is not null
BEGIN
SET @Result = null
SELECT @Result =
CASE
WHEN @Result is null
THEN ''
ELSE @Result + ', '
END + s.Name + ':' + convert(varchar(30),s.Value)
FROM @Source s
WHERE id = @id
UPDATE @Target
SET Result = @Result
WHERE id = @id
SET @id = (SELECT MIN(id) FROM @Target WHERE @id < id)
END
SELECT *
FROM @Target
评论
另请:踢的不良习惯:认为WHILE循环不是游标
– marc_s
15年3月9日在19:17
@marc_s也许更好的批评是应该在表变量上声明PRIMARY KEY。
–艾米B
2015年3月10日在2:19
@marc_s在进一步检查时,该文章是虚假的-几乎所有关于没有IO测量的性能讨论。我确实了解了LAG-非常感谢。
–艾米B
2015年3月10日在4:34
#14 楼
让我们变得非常简单:SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
替换此行:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
与您的查询。
#15 楼
如果“分组依据”主要包含一项,则可以通过以下方式显着提高性能:SELECT
[ID],
CASE WHEN MAX( [Name]) = MIN( [Name]) THEN
MAX( [Name]) NameValues
ELSE
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
END
FROM #YourTable Results
GROUP BY ID
评论
假设您不希望列表中有重复的名称,您可能会或可能不会。
– jnm2
16年6月30日在14:35
#16 楼
没有看到任何交叉应用答案,也不需要xml提取。这与Kevin Fairchild撰写的内容略有不同。在更复杂的查询中使用起来更快,更容易: select T.ID
,MAX(X.cl) NameValues
from #YourTable T
CROSS APPLY
(select STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = T.ID)
FOR XML PATH(''))
,1,2,'') [cl]) X
GROUP BY T.ID
评论
如果不使用Value,我们可能会遇到文本为XML编码字符的问题
– vCillusion
18年6月2日在22:20
#17 楼
使用Replace Function和FOR JSON PATHSELECT T3.DEPT, REPLACE(REPLACE(T3.ENAME,'{"ENAME":"',''),'"}','') AS ENAME_LIST
FROM (
SELECT DEPT, (SELECT ENAME AS [ENAME]
FROM EMPLOYEE T2
WHERE T2.DEPT=T1.DEPT
FOR JSON PATH,WITHOUT_ARRAY_WRAPPER) ENAME
FROM EMPLOYEE T1
GROUP BY DEPT) T3
有关示例数据和更多方式,请单击此处
#18 楼
如果启用了clr,则可以使用来自GitHub的Group_Concat库#19 楼
使用Stuff和for xml路径运算符将行连接到字符串:按两列分组->CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',5)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
-- retrieve each unique id and name columns and concatonate the values into one column
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES EACH APPLICATION : VALUE SET
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
SELECT
[ID],[Name] , --these are acting as the group by clause
STUFF((
SELECT ', '+ CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES THE VALUES FOR EACH ID NAME COMBINATION
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID, name
DROP TABLE #YourTable
#20 楼
另一个没有垃圾的示例:“,TYPE).value('(.. text())[1]','VARCHAR(MAX)')”WITH t AS (
SELECT 1 n, 1 g, 1 v
UNION ALL
SELECT 2 n, 1 g, 2 v
UNION ALL
SELECT 3 n, 2 g, 3 v
)
SELECT g
, STUFF (
(
SELECT ', ' + CAST(v AS VARCHAR(MAX))
FROM t sub_t
WHERE sub_t.g = main_t.g
FOR XML PATH('')
)
, 1, 2, ''
) cg
FROM t main_t
GROUP BY g
输入输出是
************************* -> *********************
* n * g * v * * g * cg *
* - * - * - * * - * - *
* 1 * 1 * 1 * * 1 * 1, 2 *
* 2 * 1 * 2 * * 2 * 3 *
* 3 * 2 * 3 * *********************
*************************
评论
通过MySQL的GROUP_CONCAT()聚合函数可以轻松解决这种类型的问题,但是在Microsoft SQL Server上解决此问题比较麻烦。请参阅以下SO问题以寻求帮助:“如何基于关系针对一个记录获取多个记录?”具有Microsoft帐户的每个人都应该对连接上的一种更简单的解决方案投票:connect.microsoft.com/SQLServer/feedback/details/427987/…
在增强T-SQL之前,可以使用此处找到的SQLCLR聚合作为替代:groupconcat.codeplex.com
复制了stackoverflow.com/questions/194852/…