如何在SQL server中将多行中的文本连接到单个文本字符串中?

如何在SQL server中将多行中的文本连接到单个文本字符串中?,sql,sql-server,csv,string-concatenation,group-concat,Sql,Sql Server,Csv,String Concatenation,Group Concat,考虑一个包含名称的数据库表,它有三行: Peter Paul Mary 有没有一种简单的方法可以将其转换为一个由Peter、Paul、Mary组成的字符串?在MySQL中,有一个函数,允许您将多行中的值串联起来。例如: 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 我在家里没有访问SQL Serv

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

Peter
Paul
Mary

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

在MySQL中,有一个函数,允许您将多行中的值串联起来。例如:

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

我在家里没有访问SQL Server的权限,因此我猜测这里的语法,但大致是:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names
此答案可能返回一致的结果,请使用其他答案中详细说明的For XML PATH方法之一。

使用
合并

DECLARE@Names VARCHAR(8000)
选择@Names=COALESCE(@Names+,,“”)+Name
来自人们
只是一些解释(因为这个答案似乎得到了相对固定的观点):

  • Coalesce实际上只是一个有用的骗局,它完成了两件事:
1) 无需使用空字符串值初始化
@Names

2) 无需在末端剥离额外的分离器

  • 如果一行的名称值为空,上述解决方案将给出不正确的结果(如果存在空值,空值将使该行之后的
    @Names
    为空,下一行将再次作为空字符串开始。使用以下两种解决方案之一可轻松修复:
DECLARE@Names VARCHAR(8000)
选择@Names=COALESCE(@Names+,,“”)+Name
来自人们
其中Name不为NULL
或:

DECLARE@Names VARCHAR(8000)
选择@Names=COALESCE(@Names+,','')+
ISNULL(名称“不适用”)
来自人们
根据您想要的行为(第一个选项只是过滤空值,第二个选项使用标记消息将它们保留在列表中[用适合您的任何内容替换“N/a”)

这会在开头加上一个逗号

但是,如果需要其他列,或者要创建子表,则需要将其包装在标量用户定义字段(UDF)中


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

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

当我试图连接两个具有一对多关系的表时,我遇到了类似的问题。在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]
如果可以在开头加上逗号,然后使用
子字符串
跳过第一个字符串,这样就不需要执行子查询,那么您可以以更紧凑的方式执行相同的操作:

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
在里面
在SQL Server 2016中 你可以使用

i、 e

结果会变成

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com
即使您的数据包含无效的XML字符,这也会起作用

'“},{u:'”
是安全的,因为如果数据包含
'“},{u:'”,
它将被转义到
“},{\”:\“

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


在SQL Server 2017中,Azure SQL数据库
您可以使用新的

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

假设表名为NameList,其中一列名为FName

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

"Peter, Paul, Mary, "
只需处理额外的逗号

编辑:根据@nreligh的注释,您可以使用以下方法删除尾随逗号。假设表名和列名相同:

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

使用XML帮助我用逗号分隔行。对于多余的逗号,我们可以使用SQLServer的replace函数。使用AS“data()”将行与空格连接起来,而不是添加逗号,后面可以用逗号替换,语法如下所示

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

在Oracle中,它是
wm_concat
。我相信此函数在和更高版本中可用。

我通常使用类似这样的选择来连接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
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

在SQLServer2005及更高版本中,使用下面的查询连接行

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

如果您想处理空值,可以通过添加where子句或在第一个子句周围添加另一个合并来完成

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

我真的很喜欢它的优雅。只是想让它完整

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

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

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

即用解决方案,无需额外的逗号:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"
空列表将导致空值。 通常,您会将列表插入表列或程序变量:根据需要调整255最大长度


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

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

警告
如果生成的字符串可能超过4000个字符,请小心执行此函数。它将引发异常。如果是这种情况,则需要处理异常或滚动您自己的函数,以防止连接的字符串超过4000个字符。

建议使用递归CTE解决方案,but未提供任何代码。下面的代码是递归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

Postgres数组非常棒。例如:

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
创建一些测试数据:

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开始,Oracle DBs就是这样。

请参见以下问题:

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

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
正如@user762952所指出的,并且根据Oracle的文档,WM_CONCAT()函数也是一个选项
test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)
SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names
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
___________________________
| 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
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
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 
DECLARE @char VARCHAR(MAX);

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

PRINT @char;
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;
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
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
SELECT CarNamesString = STUFF((
         SELECT ',' + [Name]
            FROM tbl_cars 
            FOR XML PATH('')
         ), 1, 1, '')
SELECT [Name],
       GROUP_CONCAT(DISTINCT [Name]  SEPARATOR ',')
       FROM tbl_cars
SELECT
VendorId,STRING_AGG(FirstName,',') UsersName FROM
Users
where VendorId!=9 GROUP BY VendorId