如何在SQL Server中将多行文本连接成单个文本字符串

2398

考虑一个包含姓名的数据库表格,有三行:

Peter
Paul
Mary

有没有简单的方法将这些内容转换成一个字符串:Peter, Paul, Mary

29
如需针对SQL Server的特定答案,请尝试此问题 - Matt Hamilton
22
对于MySQL,请查看此答案中的Group_Concat。它可以将多行合并为一个字段。 - Pykler
31
希望SQL Server的下一个版本能够添加一项新功能,以优雅地解决多行字符串连接的问题,而不需要使用 FOR XML PATH 这种繁琐方式。 - Pete Alvin
4
不是SQL,但如果这只是一次性的事情,你可以将列表粘贴到这个在线工具convert.town/column-to-comma-separated-list中进行转换。 - Stack Man
4
在Oracle数据库中,你可以在11g r2版本之后使用LISTAGG(COLUMN_NAME)函数,它可以实现与旧版中不被支持的WM_CONCAT(COLUMN_NAME)函数相同的功能。 - Richard
显示剩余5条评论
48个回答

11

除了Chris Shaffer的回答之外:

如果您的数据可能会重复,例如

Tom
Ali
John
Ali
Tom
Mike

使用DISTINCT可以避免重复,得到 Tom,Ali,John,Mike,而不是 Tom,Ali,John,Ali,Tom,Mike

DECLARE @Names VARCHAR(8000)
SELECT DISTINCT @Names = COALESCE(@Names + ',', '') + Name
FROM People
WHERE Name IS NOT NULL
SELECT @Names

10

MySQL完整示例:

我们有许多数据的用户,并且我们希望输出一个列表,其中可以看到所有用户的数据:

结果:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

表格设置:

CREATE TABLE `Data` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;


INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);


CREATE TABLE `User` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `User` (`id`) VALUES
(0),
(1);

查询:

SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id

谢谢!我建议进行编辑以指出GROUP BY的重要性。 - Josh

8
为避免 null 值,您可以使用 CONCAT() 函数。
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

知道 CONCAT 如何工作会很好。提供 MSDN 链接会更好。 - Reversed Engineer

8

我非常欣赏Dana的回答的优雅,只是想让它更完整。

DECLARE @names VARCHAR(MAX)
SET @names = ''

SELECT @names = @names + ', ' + Name FROM Names

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)

如果您要删除最后两个符号“,”,那么您需要在Name之后添加“,”('SELECT @names = @names + Name + ', ' FROM Names')。这样最后两个字符将始终为“,”。 - JT_
在我的情况下,我需要摆脱前导逗号,所以将查询更改为 SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names,然后您就不必之后截断它了。 - Tian van Heerden

8
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

这将把杂乱的逗号放在开头。

然而,如果你需要其他列,或者将子表格转换为CSV格式,你需要将其包装在标量用户定义字段(UDF)中。

你也可以在SELECT子句中使用XML路径作为相关子查询(但我必须等到回到工作岗位,因为Google不支持家庭工作内容 :-))


7
这个答案需要在服务器上拥有一些特权才能运行。 程序集 对您来说是一个不错的选择。有很多网站可以解释如何创建它。我认为讲解得非常清楚明了的是这个 网站
如果您想要,我已经创建了该程序集,并且可以在 此处 下载 DLL 文件。
下载后,您需要在 SQL Server 中运行以下脚本:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE;
EXEC sp_configure 'clr strict security', 1;
RECONFIGURE;

CREATE Assembly concat_assembly
   AUTHORIZATION dbo
   FROM '<PATH TO Concat.dll IN SERVER>'
   WITH PERMISSION_SET = SAFE;
GO

CREATE AGGREGATE dbo.concat (

    @Value NVARCHAR(MAX)
  , @Delimiter NVARCHAR(4000)

) RETURNS NVARCHAR(MAX)
EXTERNAL Name concat_assembly.[Concat.Concat];
GO

sp_configure 'clr enabled', 1;
RECONFIGURE

请注意,程序集的路径可能对服务器可见。由于您已经成功完成了所有步骤,因此可以像这样使用该函数:
SELECT dbo.Concat(field1, ',')
FROM Table1

SQL Server 2017 起,可以使用 STRING_AGG 函数。


1
DLL链接是404错误。 使用程序集来完成这个任务有些过度。请参考SQL Server的最佳答案 - Protiguous

6

如果你想处理null值,可以通过添加where子句或在第一个COALESCE周围添加另一个COALESCE来实现。

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People

6

我通常在SQL Server中使用如下的select语句来拼接字符串:

with lines as 
( 
  select 
    row_number() over(order by id) id, -- id is a line id
    line -- line of text.
  from
    source -- line source
), 
result_lines as 
( 
  select 
    id, 
    cast(line as nvarchar(max)) line 
  from 
    lines 
  where 
    id = 1 
  union all 
  select 
    l.id, 
    cast(r.line + N', ' + l.line as nvarchar(max))
  from 
    lines l 
    inner join 
    result_lines r 
    on 
      l.id = r.id + 1 
) 
select top 1 
  line
from
  result_lines
order by
  id desc

6
在Oracle中,它是wm_concat。我相信这个函数在10g release及更高版本中可用。

6

针对Oracle数据库,请参考以下问题:如何在Oracle中将多行合并为一行,而不创建存储过程?

最佳答案似乎是由@Emmanuel提供的,使用内置的LISTAGG()函数,该函数在Oracle 11g Release 2及更高版本中可用。

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

正如@user762952所指出的,根据Oracle文档http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php,WM_CONCAT()函数也是一种选择。它似乎是稳定的,但Oracle明确建议不要在任何应用程序SQL中使用它,因此请自行决定是否使用。
除此之外,您将需要编写自己的函数;上面提到的Oracle文档有关于如何编写自己函数的指南。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接