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

Posted

技术标签:

【中文标题】如何在 SQL Server 中将多行文本连接成单个文本字符串【英文标题】:How to concatenate text from multiple rows into a single text string in SQL Server 【发布时间】:2010-09-16 17:29:13 【问题描述】:

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

Peter
Paul
Mary

有没有一种简单的方法可以将其转换为 Peter, Paul, Mary 的单个字符串?

【问题讨论】:

对于特定于 SQL Server 的答案,请尝试this question。 对于 mysql,请查看 Group_Concat from this answer 我希望 SQL Server 的下一个版本能够提供一个新功能来优雅地解决多行字符串连接,而不会像 FOR XML PATH 那样愚蠢。 不是 SQL,但如果这是一次性的,您可以将列表粘贴到此浏览器工具中 convert.town/column-to-comma-separated-list 在 Oracle 中,您可以使用 11g r2 中的 LISTAGG(COLUMN_NAME),之前有一个不受支持的名为 WM_CONCAT(COLUMN_NAME) 的函数可以执行相同的操作。 【参考方案1】:

如果您使用的是 SQL Server 2017 或 Azure,请参阅Mathieu Renda answer。

当我尝试连接两个具有一对多关系的表时,我遇到了类似的问题。在 SQL 2005 中,我发现XML PATH 方法可以非常轻松地处理行的连接。

如果有一个表叫STUDENTS

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

我预期的结果是:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

我使用了以下T-SQL

SELECT Main.SubjectID,
       LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
    (
        SELECT DISTINCT ST2.SubjectID, 
            (
                SELECT ST1.StudentName + ',' AS [text()]
                FROM dbo.Students ST1
                WHERE ST1.SubjectID = ST2.SubjectID
                ORDER BY ST1.SubjectID
                FOR XML PATH (''), TYPE
            ).value('text()[1]','nvarchar(max)') [Students]
        FROM dbo.Students ST2
    ) [Main]

如果您可以在开头连接逗号并使用substring 跳过第一个逗号,那么您可以以更紧凑的方式执行相同的操作,这样您就不需要进行子查询:

SELECT DISTINCT ST2.SubjectID, 
    SUBSTRING(
        (
            SELECT ','+ST1.StudentName  AS [text()]
            FROM dbo.Students ST1
            WHERE ST1.SubjectID = ST2.SubjectID
            ORDER BY ST1.SubjectID
            FOR XML PATH (''), TYPE
        ).value('text()[1]','nvarchar(max)'), 2, 1000) [Students]
FROM dbo.Students ST2

【讨论】:

很好的解决方案。如果您需要处理 html 中的特殊字符,以下内容可能会有所帮助:Rob Farley: Handling special characters with FOR XML PATH('')。 显然,如果名称中包含诸如<& 之类的XML 字符,这将不起作用。请参阅@BenHinman 的评论。 注意:此方法依赖于 FOR XML PATH ('') 的未记录行为。这意味着它不应该被认为是可靠的,因为任何补丁或更新都可能改变它的功能。它基本上依赖于已弃用的功能。 @Whelkaholism 底线是FOR XML 旨在生成 XML,而不是连接任意字符串。这就是为什么它将&<> 转义为XML 实体代码(&<>)。我认为它也会在属性中将"' 转义为"'不是 GROUP_CONCAT()string_agg()array_agg()listagg() 等,即使你可以做到这一点。我们应该花时间要求微软实现适当的功能。 好消息:MS SQL Server will be adding string_agg in v.Next.,这一切都会过去。【参考方案2】:

此答案可能会返回 unexpected results 为了获得一致的结果,请使用其他答案中详述的 FOR XML PATH 方法之一。

使用COALESCE:

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

只是一些解释(因为这个答案似乎得到了相对常规的观点):

Coalesce 真的只是一个有用的作弊工具,它完成了两件事:

1) 无需使用空字符串值初始化@Names

2) 无需在末尾去掉多余的分隔符。

如果一行有一个 NULL 名称值,上述解决方案将给出不正确的结果(如果有一个 NULLNULL 将使@Names NULL 在该行之后,下一行将再次作为空字符串重新开始。使用以下两种解决方案之一轻松修复:
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

或:

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

根据您想要的行为(第一个选项只是将 NULL 过滤掉,第二个选项使用标记消息将它们保留在列表中 [将“N/A”替换为适合你])。

【讨论】:

需要明确的是,coalesce 与创建列表无关,它只是确保不包含 NULL 值。 @Graeme Perrow 它不排除 NULL 值(需要 WHERE - 如果输入值之一为 NULL,这将丢失结果),并且它在这种方法中是必需的,因为:NULL + non-NULL -> NULL 和 non-NULL + NULL -> NULL; @Name 默认为 NULL,事实上,该属性在此处用作隐式标记,以确定是否应添加 ', '。 请注意,这种连接方法依赖于 SQL Server 使用特定计划执行查询。我被这个方法发现了(加上一个 ORDER BY)。当它处理少量的行时,它工作得很好,但是对于更多的数据,SQL Server 选择了一个不同的计划,这导致选择第一个项目而没有任何连接。请参阅 Anith Sen 的 this article。 此方法不能用作选择列表或 where 子句中的子查询,因为它使用 tSQL 变量。在这种情况下,您可以使用@Ritesh 提供的方法 这不是一种可靠的连接方法。它不受支持且不应使用(根据 Microsoft,例如 support.microsoft.com/en-us/kb/287515、connect.microsoft.com/SQLServer/Feedback/Details/704389)。它可以毫无预警地改变。使用***.com/questions/5031204/… 中讨论的 XML PATH 技术我在这里写了更多:marc.durdin.net/2015/07/…【参考方案3】:

SQL Server 2017+ 和 SQL Azure:STRING_AGG

从 SQL Server 的下一个版本开始,我们终于可以跨行连接,而无需求助于任何变量或 XML 巫术。

STRING_AGG (Transact-SQL)

不分组

SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;

带分组:

SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

带有分组和子排序

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

【讨论】:

而且,与 CLR 解决方案不同,您可以控制排序。 STRING_AGG 似乎有 4000 个字符的显示限制 有没有办法在没有 GROUP BY 的情况下进行排序(所以对于“无分组”示例)? 更新:我设法做到了以下几点,但有没有更清洁的方法? SELECT STRING_AGG(Name, ', ') AS Departments FROM (SELECT TOP 100000 Name FROM HumanResources.Department ORDER BY Name) D; 我必须将它转换为 NVarchar(max) 才能使其工作.. ``` SELECT STRING_AGG(CAST(EmpName as NVARCHAR(MAX)), ',') FROM EmpTable as t `` `【参考方案4】:

一种尚未通过 SQL Server 中的 XML data() 命令显示的方法是:

假设一个名为 NameList 的表有一个名为 FName 的列,

SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')

返回:

"Peter, Paul, Mary, "

只需要处理多余的逗号。

从@NReilingh 的评论中采用,您可以使用以下方法删除尾随逗号。假设表名和列名相同:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

【讨论】:

天哪,这太棒了!当单独执行时,如您的示例中,结果被格式化为超链接,当单击时(在 SSMS 中)会打开一个包含数据的新窗口,但当用作更大查询的一部分时,它只会显示为字符串。它是一个字符串吗?还是我需要在将使用这些数据的应用程序中区别对待 xml? 这种方法还可以对 等字符进行 XML 转义。因此,选择 '' + FName + '' 会导致“<b>John</b><b>Paul...” 简洁的解决方案。我注意到即使我不添加+ ', ',它仍然会在每个连接的元素之间添加一个空格。 @Baodad 这似乎是交易的一部分。您可以通过替换添加的标记字符来解决问题。例如,这是一个完美的以逗号分隔的任意长度列表:SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'') 哇,实际上在我使用 data() 的测试中,替换的性能比没有的要好得多。超级奇怪。【参考方案5】:

在SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

在 SQL Server 2016 中

您可以使用FOR JSON syntax

SELECT per.ID,
Emails = JSON_VALUE(
   REPLACE(
     (SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
    ,'","_":"',', '),'$[0]._'
) 
FROM Person per

结果会变成

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

即使您的数据包含无效的 XML 字符,这也可以工作

'","_":"' 是安全的,因为如果您的数据包含 '","_":"',,它将被转义为 ",\"_\":\"

您可以将', ' 替换为任何字符串分隔符


在 SQL Server 2017 中,Azure SQL 数据库

您可以使用新的STRING_AGG function

【讨论】:

善用 STUFF 函数来消除前两个字符。 我最喜欢这个解决方案,因为我可以通过附加“as 这比接受的答案更好,因为此选项还处理未转义的 XML 保留字符,例如 &lt;&gt;&amp; 等,FOR XML PATH('') 将自动转义。 【参考方案6】:

在 MySQL 中,有一个函数 GROUP_CONCAT(),它允许您连接多行的值。示例:

SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people 
FROM users 
WHERE id IN (1,2,3) 
GROUP BY a

【讨论】:

基本有效。需要考虑两件事:1)如果您的列不是CHAR,则需要强制转换它,例如通过GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',') 2)如果你有很多值,你应该增加group_concat_max_len 写在***.com/a/1278210/1498405【参考方案7】:

使用 COALESCE - Learn more from here

举个例子:

102

103

104

然后在 SQL Server 中编写以下代码,

Declare @Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers
SELECT  @Numbers = COALESCE(@Numbers + ',', '') + Number
FROM   TableName where Number IS NOT NULL

SELECT @Numbers

输出将是:

102,103,104

【讨论】:

这确实是 IMO 的最佳解决方案,因为它避免了 FOR XML 呈现的编码问题。我使用了Declare @Numbers AS Nvarchar(MAX),效果很好。你能解释一下为什么你不建议使用它吗? 这个解决方案已经在8年前发布了! ***.com/a/194887/986862 为什么这个查询返回???符号而不是西里尔字母?这只是输出问题吗? @EvilDr 您可以避免 XML 编码。见:***.com/questions/15643683/… 为什么不使用问题中的示例?【参考方案8】:

PostgreSQL 数组很棒。示例:

创建一些测试数据:

postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');
INSERT 0 3
test=# select * from names;
 name
-------
 Peter
 Paul
 Mary
(3 rows)

将它们聚合成一个数组:

test=# select array_agg(name) from names;
 array_agg
-------------------
 Peter,Paul,Mary
(1 row)

将数组转换为逗号分隔的字符串:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

完成

从 PostgreSQL 9.0 开始就更容易了,引用“无名的马”的已删除答案:

select string_agg(name, ',') 
from names;

【讨论】:

如果您需要多列,例如括号中的员工 ID,请使用 concat 运算符:select array_to_string(array_agg(name||'('||id||')' 不适用于sql-server,仅适用于mysql【参考方案9】:

Oracle 11g 第 2 版支持 LISTAGG 函数。文档here。

COLUMN employees FORMAT A50

SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM   emp
GROUP BY deptno;

    DEPTNO EMPLOYEES
---------- --------------------------------------------------
        10 CLARK,KING,MILLER
        20 ADAMS,FORD,JONES,SCOTT,SMITH
        30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

3 rows selected.

警告

如果结果字符串有可能超过 4000 个字符,请小心执行此函数。它会抛出异常。如果是这种情况,那么您需要处理异常或滚动您自己的函数来防止连接的字符串超过 4000 个字符。

【讨论】:

对于旧版本的 Oracle,wm_concat 是完美的。 Alex 在链接礼物中解释了它的使用。谢谢亚历克斯! LISTAGG 完美!只需阅读此处链接的文档。 wm_concat 从版本 12c 开始删除。【参考方案10】:

在 SQL Server 2005 及更高版本中,使用下面的查询来连接行。

DECLARE @t table
(
    Id int,
    Name varchar(10)
)
INSERT INTO @t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d' 

SELECT ID,
stuff(
(
    SELECT ','+ [Name] FROM @t WHERE Id = t.Id FOR XML PATH('')
),1,1,'') 
FROM (SELECT DISTINCT ID FROM @t ) t

【讨论】:

我相信当值包含诸如&lt;&amp;之类的XML符号时,这会失败。 在提供的示例中效果很好。我使用 CTE docs.microsoft.com/en-us/sql/t-sql/queries/… 而不是临时表或变量【参考方案11】:

建议使用递归CTE 解决方案,但未提供代码。下面的代码是递归 CTE 的示例。

请注意,虽然结果与问题匹配,但数据与给定的描述不完全匹配,因为我假设您确实希望对行组执行此操作,而不是对所有行桌子。将其更改为匹配表中的所有行留给读者作为练习。

;WITH basetable AS (
    SELECT
        id,
        CAST(name AS VARCHAR(MAX)) name,
        ROW_NUMBER() OVER (Partition BY id ORDER BY seq) rw,
        COUNT(*) OVER (Partition BY id) recs
    FROM (VALUES
        (1, 'Johnny', 1),
        (1, 'M', 2),
        (2, 'Bill', 1),
        (2, 'S.', 4),
        (2, 'Preston', 5),
        (2, 'Esq.', 6),
        (3, 'Ted', 1),
        (3, 'Theodore', 2),
        (3, 'Logan', 3),
        (4, 'Peter', 1),
        (4, 'Paul', 2),
        (4, 'Mary', 3)
    ) g (id, name, seq)
),
rCTE AS (
    SELECT recs, id, name, rw
    FROM basetable
    WHERE rw = 1

    UNION ALL

    SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw + 1
    FROM basetable b
    INNER JOIN rCTE r ON b.id = r.id AND b.rw = r.rw + 1
)
SELECT name
FROM rCTE
WHERE recs = rw AND ID=4

【讨论】:

对于大吃一惊:此查询将 12 行(3 列)插入到临时基表中,然后创建递归公用表表达式 (rCTE),然后将 name 列展平为逗号分隔ids 的 4 个 的字符串。乍一看,我认为这比大多数其他 SQL Server 解决方案所做的工作更多。 @knb: 不确定这是赞美、谴责还是只是惊讶。基表是因为我喜欢我的示例实际工作,它与问题没有任何关系。 谢谢你。我一直想知道这种麦芽汁是否可能。【参考方案12】:

我家里没有 SQL Server 的访问权限,所以我猜测这里的语法,但或多或​​少:

DECLARE @names VARCHAR(500)

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

【讨论】:

您需要将 @names 初始化为非空值,否则您将始终获得 NULL;您还需要处理分隔符(包括不必要的分隔符) 这种方法(我一直在使用)的唯一问题是您无法嵌入它 要去掉前导空格,将查询更改为SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names 另外,您必须检查 Name 是否为空,您可以这样做:SELECT @names = @names + ISNULL(' ' + Name, '')【参考方案13】:

您需要创建一个变量来保存您的最终结果并选择其中,就像这样。

最简单的解决方案

DECLARE @char VARCHAR(MAX);

SELECT @char = COALESCE(@char + ', ' + [column], [column]) 
FROM [table];

PRINT @char;

【讨论】:

【参考方案14】:

在 SQL Server vNext 中,这将使用 STRING_AGG 函数内置。在 STRING_AGG (Transact-SQL) 中了解更多信息。

【讨论】:

【参考方案15】:

即用型解决方案,没有额外的逗号:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

空列表将导致 NULL 值。 通常您会将列表插入到表格列或程序变量中:根据需要调整 255 的最大长度。

(Diwakar 和 Jens Frandsen 提供了很好的答案,但需要改进。)

【讨论】:

使用这个时逗号前有一个空格:( 如果您不想要额外的空间,只需将 ', ' 替换为 ','【参考方案16】:

使用 XML 帮助我用逗号分隔行。对于多余的逗号,我们可以使用 SQL Server 的替换功能。代替添加逗号,使用 AS 'data()' 将使用空格连接行,稍后可以用逗号替换,如下所示。

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 

【讨论】:

这是我认为最好的答案。当您需要加入另一个表时,使用声明变量并不好,这很好而且很短。干得好。 如果 FName 数据已经有空格,例如“我的名字”,那就不行了 真的它在 ms-sql 2016 上对我有用 Select REPLACE( (select Name AS 'data()' from Brand Where Id IN (1,2,3,4) for xml path(' ')) , ' ', ', ') as allBrands【参考方案17】:

在SQL Server 2017 或更高版本中,您可以使用STRING_AGG() 函数生成逗号分隔 值。请看下面的一个例子。

SELECT
VendorId,STRING_AGG(FirstName,',') UsersName FROM
Users
where VendorId!=9 GROUP BY VendorId

【讨论】:

【参考方案18】:

这对我有用(SQL Server 2016):

SELECT CarNamesString = STUFF((
         SELECT ',' + [Name]
            FROM tbl_cars
            FOR XML PATH('')
         ), 1, 1, '')

这里是来源:https://www.mytecbits.com/

MySQL 的解决方案(因为此页面出现在 Google for MySQL 中):

SELECT [Name],
       GROUP_CONCAT(DISTINCT [Name]  SEPARATOR ',')
       FROM tbl_cars

来自MySQL documentation。

【讨论】:

【参考方案19】:
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

这是一个示例:

DECLARE @t TABLE (name VARCHAR(10))
INSERT INTO @t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM @t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary

【讨论】:

【参考方案20】:

对于其他答案,阅读答案的人必须知道特定的域表,例如车辆或学生。必须创建该表并用数据填充该表以测试解决方案。

以下是使用 SQL Server“Information_Schema.Columns”表的示例。通过使用此解决方案,无需创建表或添加数据。此示例为数据库中的所有表创建一个以逗号分隔的列名列表。

SELECT
    Table_Name
    ,STUFF((
        SELECT ',' + Column_Name
        FROM INFORMATION_SCHEMA.Columns Columns
        WHERE Tables.Table_Name = Columns.Table_Name
        ORDER BY Column_Name
        FOR XML PATH ('')), 1, 1, ''
    )Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME 

【讨论】:

【参考方案21】:
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

这会将杂散的逗号放在开头。

但是,如果您需要其他列,或将子表 CSV 保存在一个标量用户定义字段 (UDF) 中。

您也可以在 SELECT 子句中使用 XML 路径作为相关子查询(但我必须等到我回去工作,因为 Google 不在家做工作 :-)

【讨论】:

【参考方案22】:

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 的重要性【参考方案23】:

对于 Oracle 数据库,请参阅此问题:How can multiple rows be concatenated into one in Oracle without creating a stored procedure?

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

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 文档提供了如何执行此操作的指南。

【讨论】:

【参考方案24】:

为避免空值,您可以使用 CONCAT()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

【讨论】:

很高兴知道为什么 CONCAT 有效。到 MSDN 的链接会很好。【参考方案25】:

我真的很喜欢 Dana's answer 的优雅,只是想让它变得完整。

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') .这样最后两个字符将始终是 ', '。 在我的情况下,我需要去掉 前导 逗号,因此将查询更改为 SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names,然后您就不必在之后截断它。【参考方案26】:

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

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

【讨论】:

【参考方案27】:

这个答案需要服务器上的一些权限才能工作。

Assemblies 对您来说是个不错的选择。有很多网站解释了如何创建它。我认为解释得很好的是one。

如果需要,我已经创建了程序集,可以下载DLL文件here。

下载后,您需要在 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 函数。

【讨论】:

DLL 链接是 404 错误。为此使用程序集是矫枉过正的。有关 SQL Server,请参阅 best answer。【参考方案28】:

我通常使用这样的 select 在 SQL Server 中连接字符串:

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

【讨论】:

【参考方案29】:

在Chris Shaffer's answer之上:

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

Tom
Ali
John
Ali
Tom
Mike

而不是Tom,Ali,John,Ali,Tom,Mike

您可以使用 DISTINCT 避免重复并获取Tom,Ali,John,Mike

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

【讨论】:

【参考方案30】:

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

【讨论】:

以上是关于如何在 SQL Server 中将多行文本连接成单个文本字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Microsoft SQL Server Management Studio 中将多行合并为一行,用逗号分隔

SQL Server中将多行数据拼接为一行数据(一个字符串

在 SQL Server 中将多行动态组合成多列

在 SQL Server 中将数据从一列拆分为多行

在SQL Server中将多行相同id的行合并为一行[关闭]

如何将多行中的文本连接成SQL服务器中的单个文本字符串String?