当前位置:  开发笔记 > 数据库 > 正文

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

如何解决《如何将多行中的文本连接成SQLServer中的单个文本字符串?》经验,为你挑选了27个好方法。

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

Peter
Paul
Mary

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



1> 小智..:

如果您使用的是SQL Server 2017或Azure,请参阅Mathieu Renda的答案.

当我尝试使用一对多关系加入两个表时,我遇到了类似的问题.在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 ('')
            ) [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 ('')
        ), 2, 1000) [Students]
FROM dbo.Students ST2


@Whelkaholism底线是"FOR XML"旨在生成XML,而不是连接任意字符串.这就是为什么它将`&`,`<`和`>`转换为XML实体代码(`&`,`<`,`>`).我假设它也会在属性中将```和`'`转换为``和`'``.它不是*`GROUP_CONCAT()`,`string_agg()`,`array_agg()`, `listagg()`等等,即使你可以做到这一点.我们*应该*花时间要求微软实现正确的功能.
注意:此方法依赖于`FOR XML PATH('')`的未记录行为.这意味着它不应被视为可靠,因为任何补丁或更新都可能改变其功能.它基本上依赖于已弃用的功能.
好的解决方案 如果您需要处理HTML中的特殊字符,以下内容可能会有所帮助:[Rob Farley:使用FOR XML PATH('')处理特殊字符](http://sqlblog.com/blogs/rob_farley/archive/2010/ 4月15日/处理 - 特殊字符 - 用换XML-path.aspx).
好消息:[MS SQL Server将在v.Next中添加`string_agg`.](http://stackoverflow.com/a/42778050),所有这些都可以消失.
如果名称包含诸如`<`或`&`之类的XML字符,显然这不起作用.见@ BenHinman的评论.

2> Chris Shaffe..:

当存在ORDER BY子句时,此答案可能会返回意外结果.要获得一致的结果,请使用其他答案中详述的FOR XML PATH方法之一.

用途COALESCE:

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

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

Coalesce真的只是一个有用的骗子,可以完成两件事:

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

2)最后无需剥去额外的分离器.

如果行具有NULL Name值,则上面的解决方案将给出不正确的结果(如果存在NULL,则NULL将在该行之后@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 s,第二个选项将它们保留在列表中,并带有标记消息[用适合您的任何内容替换'N/A']).


要清楚,coalesce与创建列表无关,它只是确保不包含NULL值.
请注意,这种连接方法依赖于SQL Server使用特定计划执行查询.我被发现使用这种方法(添加了ORDER BY).当它处理少量行时它运行正常但是有了更多数据,SQL Server选择了一个不同的计划,导致选择第一个没有连接的项目.见Anith Sen的[本文](http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/#_Toc205129492).
@Graeme Perrow它不排除NULL值(需要WHERE - 如果其中一个输入值为NULL,*将丢失结果*),并且**在此方法中是必需的*因为:NULL + non NULL - > NULL和非NULL + NULL - > NULL; 默认情况下,@ Name也是NULL,实际上,该属性在此处用作隐式标记,以确定是否应添加",".
此方法不能用作选择列表或where子句中的子查询,因为它使用tSQL变量.在这种情况下,您可以使用@Ritesh提供的方法
这不是一种可靠的连接方法.它不受支持且不应使用(根据Microsoft,例如https://support.microsoft.com/en-us/kb/287515,https://connect.microsoft.com/SQLServer/Feedback/Details/704389).它可以在没有任何警告的情 使用http://stackoverflow.com/questions/5031204/does-t-sql-have-an-aggregate-function-to-concatenate-strings/5031297#5031297中讨论的XML PATH技术我在这里写了更多:http:/ /marc.durdin.net/2015/07/concatenating-strings-in-sql-server-or-undefined-behaviour-by-design/
@krubo不,问题是`@Names = @Names +*nothing*`将为null,因为@Names在声明时为null.COALESCE解析**null`Name`值**和**初始null` @ Names`值.
@XpiritO-您是指text和ntext吗?Varchar是兼容的;可以将text和ntext转换(如果您使用的是SQL 2005,则将它们转换为VARCHAR(MAX)/ NVARCHAR(MAX),并且不会丢失任何内容;否则,无论如何,您都必须接受截断的可能性,因为可以这样) t声明一个text / ntext变量)。
这种解决方案在某种观点上是行不通的,但Ritesh的观点是可行的。
尝试过,一开始就喜欢它,因为它是如此的简单和辉煌。但是,就像对varchar()的任何其他重复concat进程一样,I / O和CPU最终会固定在一起。对于20,000个GUID,连接它们花费了2分钟,而使用`for xml path('')`则花费了不到1秒的时间。
@confusedMind-像这样:`DECLARE @Names VARCHAR(8000)SELECT @Names = COALESCE(@Names +',','')+ Name FROM(SELECT DISTINCT Name FROM People)`
这种方法的主要问题是它将截断所有超过8000的值。
您的解释并不能真正解释其作用。它依赖于SQL Server为每一行执行表达式。如果可以依靠的话那真是太好了。但是请参阅其他注释,指出对于这种类型的查询,不需要SQL Server。
真是 我对这个答案进行了投票,但是现在我知道这不是连接的正确方法,因为它是一种不受支持的功能,它会使您烦恼,我无法删除我的投票了:-(现在我误导了很多。

3> Mathieu Rend..:
SQL Server 2017+和SQL Azure:STRING_AGG

从下一版本的SQL Server开始,我们最终可以跨行连接,而不必诉诸任何变量或XML witchery.

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;


这在Azure SQL中也适用于我.辉煌!

4> 小智..:

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

假设名为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,'')`

5> Steven Chong..:

在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语法

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功能


这比接受的答案要好,因为这个选项还可以处理未转义的XML保存字符,例如```,`>`,`&`等,`FOR XML PATH('')`会自动转义.
很好地利用STUFF函数来修改前两个字符.
我最喜欢这个解决方案,因为我可以通过附加'as
推荐阅读
刘美娥94662
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有