如何将多行中的文本连接到SQLServer中的单个文本字符串

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

PeterPaulMary

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

3013132 次浏览

在SQLServer中可以这样做的一种方法是将表内容返回为XML(对于XML原始),将结果转换为字符串,然后将标记替换为“,”。

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

SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS peopleFROM usersWHERE id IN (1,2,3)GROUP BY a

我在家里无法访问SQL服务器,所以我猜这里的语法,但它或多或少:

DECLARE @names VARCHAR(500)
SELECT @names = @names + ' ' + NameFROM Names

此答案可能会返回意外结果要获得一致的结果,请使用其他答案中详述的FORXML PATH方法之一。

使用COALESCE

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

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

  • Coalesce实际上只是一个有用的欺骗,可以完成两件事:

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

2)不需要在末端剥离额外的隔板。

  • 如果一行具有NULL Name值,上面的解决方案将给出不正确的结果(如果有NULL,则NULL将在该行之后制作@NamesNULL,下一行将再次作为空字符串重新开始。使用以下两种解决方案之一很容易修复:
DECLARE @Names VARCHAR(8000)SELECT @Names = COALESCE(@Names + ', ', '') + NameFROM PeopleWHERE Name IS NOT NULL

或:

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

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

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

这把流浪逗号放在开头。

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

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

如果使用SQLServer 2017或Azure,请参阅Mathieu Renda回答

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

如果有一个名为STUDENTS的表

SubjectID       StudentName----------      -------------1               Mary1               John1               Sam2               Alaina2               Edward

我预期的结果是:

SubjectID       StudentName----------      -------------1               Mary, John, Sam2               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 ST1WHERE ST1.SubjectID = ST2.SubjectIDORDER BY ST1.SubjectIDFOR 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 ST1WHERE ST1.SubjectID = ST2.SubjectIDORDER BY ST1.SubjectIDFOR XML PATH (''), TYPE).value('text()[1]','nvarchar(max)'), 2, 1000) [Students]FROM dbo.Students ST2

SQLServer 2005

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

SQLServer 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  Emails1   abc@gmail.com2   NULL3   def@gmail.com, xyz@gmail.com

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

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

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


在SQLServer 2017中,AzureSQL数据库

您可以使用新的STRING_AGG功能

一种尚未通过SQLServer中的XMLdata()命令显示的方法是:

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

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

退货:

"Peter, Paul, Mary, "

只有多余的逗号必须处理。

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

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

使用XML帮助我用逗号分隔行。对于额外的逗号,我们可以使用SQLServer的替换功能。使用AS'data()'将用空格连接行,而不是添加逗号,稍后可以用逗号替换,如下所述的语法。

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

使用这个:

ISNULL(SUBSTRING(REPLACE((select ',' FName as 'data()' from NameList for xml path('')), ' ,',', '), 2, 300), '') 'MyList'

其中“300”可以是任何宽度,考虑到您认为将显示的最大项目数。

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

我通常使用这样的选择来连接SQLServer中的字符串:

with lines as(selectrow_number() over(order by id) id, -- id is a line idline -- line of text.fromsource -- line source),result_lines as(selectid,cast(line as nvarchar(max)) linefromlineswhereid = 1union allselectl.id,cast(r.line + N', ' + l.line as nvarchar(max))fromlines linner joinresult_lines ronl.id = r.id + 1)select top 1linefromresult_linesorder byid desc

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

DECLARE @t table(Id int,Name varchar(10))INSERT INTO @tSELECT 1,'a' UNION ALLSELECT 1,'b' UNION ALLSELECT 2,'c' UNION ALLSELECT 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子句或在第一个子句周围添加另一个COALESCE来做到这一点。

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

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

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"

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

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

Oracle 11g第2版支持LISTAGG功能。文档这里

COLUMN employees FORMAT A50
SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employeesFROM   empGROUP BY deptno;
DEPTNO EMPLOYEES---------- --------------------------------------------------10 CLARK,KING,MILLER20 ADAMS,FORD,JONES,SCOTT,SMITH30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.

警告

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

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

请注意,尽管结果与问题匹配,但数据相当与给定的描述不匹配,因为我假设您真的想对行组执行此操作,而不是表中的所有行。将其更改为匹配表中的所有行留给读者作为练习。

;WITH basetable AS (SELECTid,CAST(name AS VARCHAR(MAX)) name,ROW_NUMBER() OVER (Partition BY id ORDER BY seq) rw,COUNT(*) OVER (Partition BY id) recsFROM (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, rwFROM basetableWHERE rw = 1
UNION ALL
SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw + 1FROM basetable bINNER JOIN rCTE r ON b.id = r.id AND b.rw = r.rw + 1)SELECT nameFROM rCTEWHERE recs = rw AND ID=4OPTION (MAXRECURSION 101)

PostgreSQL数组很棒。示例:

创建一些测试数据:

postgres=# \c testYou are now connected to database "test" as user "hgimenez".test=# create table names (name text);CREATE TABLEtest=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');INSERT 0 3test=# select * from names;name-------PeterPaulMary(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;

在Oracle中有几种方法:

    create table name(first_name varchar2(30));
insert into name values ('Peter');insert into name values ('Paul');insert into name values ('Mary');

解决方案是1:

    select substr(max(sys_connect_by_path (first_name, ',')),2) from (select rownum r, first_name from name ) n start with r=1 connect by prior r+1=ro/p=> Peter,Paul,Mary

解决方案是2:

    select  rtrim(xmlagg (xmlelement (e, first_name || ',')).extract ('//text()'), ',') first_name from nameo/p=> Peter,Paul,Mary

对于Oracle DB,请参阅此问题:如何在Oracle中将多行连接成一行而不创建存储过程?

最好的答案似乎是@Emmanuel,使用内置的LISTAGG()函数,可在Oracle 11g第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文档有一个关于如何做到这一点的指南。

这也可以很有用

create table #test (id int,name varchar(10))--use separate inserts on older versions of SQL Serverinsert into #test values (1,'Peter'), (1,'Paul'), (1,'Mary'), (2,'Alex'), (3,'Jack')
DECLARE @t VARCHAR(255)SELECT @t = ISNULL(@t + ',' + name, name) FROM #test WHERE id = 1select @tdrop table #test

返回

Peter,Paul,Mary

此方法仅适用于Teradata Aster数据库,因为它使用其NPATH函数。

再一次,我们有学生

SubjectID       StudentName----------      -------------1               Mary1               John1               Sam2               Alaina2               Edward

那么对于NPATH,它只是一个SELECT:

SELECT * FROM npath(ON StudentsPARTITION BY SubjectIDORDER BY StudentNameMODE(nonoverlapping)PATTERN('A*')SYMBOLS('true' as A)RESULT(FIRST(SubjectID of A) as SubjectID,ACCUMULATE(StudentName of A) as StudentName));

结果:

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

使用“TABLE”类型非常简单。让我们假设您的表被称为Students并且它有第name列。

declare @rowsCount INTdeclare @i INT = 1declare @names varchar(max) = ''
DECLARE @MyTable TABLE(Id int identity,Name varchar(500))insert into @MyTable select name from Studentsset @rowsCount = (select COUNT(Id) from @MyTable)
while @i < @rowsCountbeginset @names = @names + ', ' + (select name from @MyTable where Id = @i)set @i = @i + 1endselect @names

此示例使用SQLServer 2008 R2进行了测试。

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

DECLARE @names VARCHAR(500)SELECT @names = CONCAT(@names, ' ', name)FROM Namesselect @names
   declare @phone varchar(max)=''select @phone=@phone + mobileno +',' from  membersselect @phone

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

大会对你来说是一个很好的选择。有很多网站解释如何创建它。我认为解释得很好的是这个一个

如果需要,我已经创建了程序集,并且可以下载DLL文件这里

下载后,您需要在SQL服务器中运行以下脚本:

EXEC sp_configure 'show advanced options', 1RECONFIGURE;EXEC sp_configure 'clr strict security', 1;RECONFIGURE;
CREATE Assembly concat_assemblyAUTHORIZATION dboFROM '<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

SQLServer 2017开始,可以使用STRING_AGG函数。

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

使用COALESCE-从这里了解更多

举个例子:

102

103

104

然后在SQLServer中编写以下代码,

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

产出将是:

102,103,104

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

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

SELECTTable_Name,STUFF((SELECT ',' + Column_NameFROM INFORMATION_SCHEMA.Columns ColumnsWHERE Tables.Table_Name = Columns.Table_NameORDER BY Column_NameFOR XML PATH ('')), 1, 1, '')ColumnsFROM INFORMATION_SCHEMA.Columns TablesGROUP BY TABLE_NAME
SELECT PageContent = Stuff((   SELECT PageContentFROM dbo.InfoGuideWHERE CategoryId = @CategoryIdAND SubCategoryId = @SubCategoryIdfor xml path(''), type).value('.[1]','nvarchar(max)'),1, 1, '')FROM dbo.InfoGuide info

SQLServer 2005或更高版本

CREATE TABLE dbo.Students(StudentId INT, Name VARCHAR(50), CONSTRAINT PK_Students PRIMARY KEY (StudentId));
CREATE TABLE dbo.Subjects(SubjectId INT, Name VARCHAR(50), CONSTRAINT PK_Subjects PRIMARY KEY (SubjectId));
CREATE TABLE dbo.Schedules(StudentId INT, SubjectId INT, CONSTRAINT PK__Schedule PRIMARY KEY (StudentId, SubjectId), CONSTRAINT FK_Schedule_Students FOREIGN KEY (StudentId) REFERENCES dbo.Students (StudentId), CONSTRAINT FK_Schedule_Subjects FOREIGN KEY (SubjectId) REFERENCES dbo.Subjects (SubjectId));
INSERT dbo.Students (StudentId, Name) VALUES(1, 'Mary'), (2, 'John'), (3, 'Sam'), (4, 'Alaina'), (5, 'Edward');
INSERT dbo.Subjects (SubjectId, Name) VALUES(1, 'Physics'), (2, 'Geography'), (3, 'French'), (4, 'Gymnastics');
INSERT dbo.Schedules (StudentId, SubjectId) VALUES(1, 1)        --Mary, Physics, (2, 1)    --John, Physics, (3, 1)    --Sam, Physics, (4, 2)    --Alaina, Geography, (5, 2)    --Edward, Geography;
SELECTsub.SubjectId, sub.Name AS [SubjectName], ISNULL( x.Students, '') AS StudentsFROMdbo.Subjects subOUTER APPLY(SELECTCASE ROW_NUMBER() OVER (ORDER BY stu.Name) WHEN 1 THEN '' ELSE ', ' END+ stu.NameFROMdbo.Students stuINNER JOIN dbo.Schedules schON stu.StudentId = sch.StudentIdWHEREsch.SubjectId = sub.SubjectIdORDER BYstu.NameFOR XML PATH('')) x (Students);

并不是说我对性能做了任何分析,因为我的列表只有不到10个项目,但在查看了30多个答案后,我感到惊讶,我仍然对已经给出的类似答案有一个扭曲,类似于对单个组列表使用COALESCE,甚至不必设置我的变量(默认值为NULL),它假设我的数据源表中的所有条目都不为空:

DECLARE @MyList VARCHAR(1000), @Delimiter CHAR(2) = ', 'SELECT @MyList = CASE WHEN @MyList > '' THEN @MyList + @Delimiter ELSE '' END + FieldToConcatenate FROM MyData

我相信COALESCE内部使用相同的想法。希望微软不要在我身上改变这一点。

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

最简单的解决方案

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

在SQLServer vNext中,它将内置STRING_AGG函数。

SQLServer 2017+和SQLAzure:STRING_AGG

从下一个版本的SQLServer开始,我们终于可以跨行连接,而无需借助任何变量或XML魔法。

STRING_AGG(Transact-SQL)

没有分组

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

分组:

SELECT GroupName, STRING_AGG(Name, ', ') AS DepartmentsFROM HumanResources.DepartmentGROUP BY GroupName;

通过分组和子排序

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS DepartmentsFROM HumanResources.DepartmentGROUP BY GroupName;

虽然为时已晚,但已经有很多解决方案。这是MySQL的简单解决方案:

SELECT t1.id,GROUP_CONCAT(t1.id) idsFROM table t1 JOIN table t2 ON (t1.id = t2.id)GROUP BY t1.id
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

下面是一个简单的PL/SQL过程,使用“基本循环”和“rownum”实现给定的场景

表定义

CREATE TABLE "NAMES" ("NAME" VARCHAR2(10 BYTE))) ;

让我们在这个表中插入值

INSERT INTO NAMES VALUES('PETER');INSERT INTO NAMES VALUES('PAUL');INSERT INTO NAMES VALUES('MARY');

程序从这里开始

DECLARE
MAXNUM INTEGER;CNTR INTEGER := 1;C_NAME NAMES.NAME%TYPE;NSTR VARCHAR2(50);
BEGIN
SELECT MAX(ROWNUM) INTO MAXNUM FROM NAMES;
LOOP
SELECT NAME INTO  C_NAME FROM(SELECT ROWNUM RW, NAME FROM NAMES ) P WHERE P.RW = CNTR;
NSTR := NSTR ||','||C_NAME;CNTR := CNTR + 1;EXIT WHEN CNTR > MAXNUM;
END LOOP;
dbms_output.put_line(SUBSTR(NSTR,2));
END;

结果

PETER,PAUL,MARY

以下是实现这一目标的完整解决方案:

-- Table CreationCREATE TABLE Tbl( CustomerCode    VARCHAR(50), CustomerName    VARCHAR(50), Type VARCHAR(50),Items    VARCHAR(50))
insert into TblSELECT 'C0001','Thomas','BREAKFAST','Milk'union SELECT 'C0001','Thomas','BREAKFAST','Bread'union SELECT 'C0001','Thomas','BREAKFAST','Egg'union SELECT 'C0001','Thomas','LUNCH','Rice'union SELECT 'C0001','Thomas','LUNCH','Fish Curry'union SELECT 'C0001','Thomas','LUNCH','Lessy'union SELECT 'C0002','JOSEPH','BREAKFAST','Bread'union SELECT 'C0002','JOSEPH','BREAKFAST','Jam'union SELECT 'C0002','JOSEPH','BREAKFAST','Tea'union SELECT 'C0002','JOSEPH','Supper','Tea'union SELECT 'C0002','JOSEPH','Brunch','Roti'
-- function creationGOCREATE  FUNCTION [dbo].[fn_GetItemsByType](@CustomerCode VARCHAR(50),@Type VARCHAR(50))RETURNS @ItemType TABLE  ( Items VARCHAR(5000) )ASBEGIN
INSERT INTO @ItemType(Items)SELECT  STUFF((SELECT distinct ',' + [Items]FROM TblWHERE CustomerCode = @CustomerCodeAND Type=@TypeFOR XML PATH('')),1,1,'') as  Items


RETURNEND
GO
-- fianl QueryDECLARE @cols AS NVARCHAR(MAX),@query  AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(Type)from TblFOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
set @query = 'SELECT CustomerCode,CustomerName,' + @cols + 'from(selectdistinct CustomerCode,CustomerName,Type,F.ItemsFROM Tbl TCROSS APPLY [fn_GetItemsByType] (T.CustomerCode,T.Type) F) xpivot(max(Items)for Type in (' + @cols + ')) p '
execute(@query)

使用递归查询,您可以这样做:

-- Create example tableCREATE TABLE tmptable (NAME VARCHAR(30)) ;
-- Insert example dataINSERT INTO tmptable VALUES('PETER');INSERT INTO tmptable VALUES('PAUL');INSERT INTO tmptable VALUES('MARY');
-- Recurse querywith tblwithrank as (select * , row_number() over(order by name) rang , count(*) over() NbRowfrom tmptable),tmpRecursive as (select *, cast(name as varchar(2000)) as AllName from tblwithrank  where rang=1union allselect f0.*,  cast(f0.name + ',' + f1.AllName as varchar(2000)) as AllNamefrom tblwithrank f0 inner join tmpRecursive f1 on f0.rang=f1.rang +1)select AllName from tmpRecursivewhere rang=NbRow

我们可以使用重复性,与CTE,联合ALL如下

declare @mytable as table(id int identity(1,1), str nvarchar(100))insert into @mytable values('Peter'),('Paul'),('Mary')
declare @myresult as table(id int,str nvarchar(max),ind int, R# int)
;with cte as(select id,cast(str as nvarchar(100)) as str, cast(0 as int) ind from @mytableunion allselect t2.id,cast(t1.str+',' +t2.str as nvarchar(100)) ,t1.ind+1 from cte t1 inner join @mytable t2 on t2.id=t1.id+1)insert into @myresult select *,row_number() over(order by ind) R# from cte
select top 1 str from @myresult order by R# desc

Chris Shaffer的回答的顶部:

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

TomAliJohnAliTomMike

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

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

DECLARE @Names VARCHAR(8000)SELECT DISTINCT @Names = COALESCE(@Names + ',', '') + NameFROM PeopleWHERE Name IS NOT NULLSELECT @Names

这对我有用(SQLServer 2016):

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

来源:https://www.mytecbits.com/

以及mysql的解决方案(因为此页面显示在Google for MySQL中):

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

MySQL留档

首先,您应该声明一个表变量并用您的表数据填充它,然后使用WHILE循环,逐一选择行并将其值添加到nvarchar(max)变量。

    Godeclare @temp table(title nvarchar(50))insert into @temp(title)select p.Title from dbo.person p--declare @mainString nvarchar(max)set @mainString = '';--while ((select count(*) from @temp) != 0)begindeclare @itemTitle nvarchar(50)set @itemTitle = (select top(1) t.Title from @temp t)    
if @mainString = ''beginset @mainString = @itemTitleendelsebeginset @mainString = concat(@mainString,',',@itemTitle)end    
delete top(1) from @temp    
endprint @mainString

在PostgreSQL中-array_agg

SELECT array_to_string(array_agg(DISTINCT rolname), ',') FROM pg_catalog.pg_roles;

STRING_AGG

SELECT STRING_AGG(rolname::text,',') FROM pg_catalog.pg_roles;

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

SELECTVendorId, STRING_AGG(FirstName,',') UsersNameFROM UsersWHERE VendorId != 9GROUP BY VendorId

在此处输入图片描述