提问人:JohnnyM 提问时间:10/12/2008 最后编辑:Peter MortensenJohnnyM 更新时间:11/7/2023 访问量:3224502
如何在 SQL Server 中将多行中的文本连接成一个文本字符串
How to concatenate text from multiple rows into a single text string in SQL Server
问:
考虑一个包含名称的数据库表,该表包含三行:
Peter
Paul
Mary
有没有一种简单的方法可以将其转换为单个字符串?Peter, Paul, Mary
答:
在 SQL Server 中执行此操作的一种方法是将表内容作为 XML(对于 XML 原始)返回,将结果转换为字符串,然后将标记替换为“,”。
在 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
评论
CHAR
GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
group_concat_max_len
我在家里无法访问 SQL Server,所以我猜测这里的语法,但它或多或少是:
DECLARE @names VARCHAR(500)
SELECT @names = @names + ' ' + Name
FROM Names
评论
SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
SELECT @names = @names + ISNULL(' ' + Name, '')
此答案可能会返回意外结果 为了获得一致的结果,请使用其他答案中详述的 FOR XML PATH 方法之一。
用:COALESCE
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
只是一些解释(因为这个答案似乎得到了相对常规的观点):
- Coalesce 实际上只是一个有用的作弊工具,可以完成两件事:
1) 无需使用空字符串值进行初始化。@Names
2)无需在最后剥离额外的分离器。
- 如果一行具有 NULL Name 值,则上述解决方案将给出不正确的结果(如果存在 NULL,则 NULL 将在该行之后变为 NULL,下一行将再次作为空字符串重新开始。使用以下两种解决方案之一轻松修复:
@Names
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”替换为适合您的任何内容])。
评论
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)
这会将杂散逗号放在开头。
但是,如果需要其他列,或者要对子表进行 CSV 操作,则需要将其包装在标量用户定义字段 (UDF) 中。
您也可以在 SELECT 子句中使用 XML 路径作为相关子查询(但我必须等到我回去工作,因为 Google 不会在家做工作:-)
如果使用的是 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 (''), 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
评论
<
&
FOR XML PATH ('')
FOR XML
&
<
>
&
<
>
"
'
"
'
GROUP_CONCAT()
string_agg()
array_agg()
listagg()
string_agg
,所有这些都可以消失。
在 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 [email protected]
2 NULL
3 [email protected], [email protected]
即使您的数据包含无效的 XML 字符,这也将起作用
是安全的,因为如果您的数据包含它将被转义为'"},{"_":"'
'"},{"_":"',
"},{\"_\":\"
您可以替换为任何字符串分隔符', '
在 SQL Server 2017 中,Azure SQL 数据库
您可以使用新的STRING_AGG功能
评论
<
>
&
FOR XML PATH('')
尚未通过 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
评论
+ ', '
SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
使用 XML 帮助我用逗号分隔行。对于额外的逗号,我们可以使用 SQL Server 的 replace 函数。使用 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 中,它是 .我相信此功能在 10g 及更高版本中可用。wm_concat
我通常使用这样的 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
在 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
评论
<
&
如果要处理空值,可以通过添加 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 @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names
即用型解决方案,无需额外的逗号:
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 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 个字符。
评论
LISTAGG
效果完美!只需阅读此处链接的文档即可。 从版本 12c 开始删除。wm_concat
提出了递归 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
OPTION (MAXRECURSION 101)
评论
name
id
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;
评论
select array_to_string(array_agg(name||'('||id||')'
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=r
o/p=> Peter,Paul,Mary
解决方案是 2:
select rtrim(xmlagg (xmlelement (e, first_name || ',')).extract ('//text()'), ',') first_name from name
o/p=> Peter,Paul,Mary
对于 Oracle 数据库,请参阅以下问题:如何在不创建存储过程的情况下在 Oracle 中将多行连接为一行?
最好的答案似乎是使用内置的 LISTAGG() 函数进行@Emmanuel,该函数在 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 Server
insert 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 = 1
select @t
drop table #test
返回
Peter,Paul,Mary
评论
此方法仅适用于 Teradata Aster 数据库,因为它使用其 NPATH 函数。
同样,我们有表 Students
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
然后使用 NPATH,它只是单个 SELECT:
SELECT * FROM npath(
ON Students
PARTITION BY SubjectID
ORDER BY StudentName
MODE(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 INT
declare @i INT = 1
declare @names varchar(max) = ''
DECLARE @MyTable TABLE
(
Id int identity,
Name varchar(500)
)
insert into @MyTable select name from Students
set @rowsCount = (select COUNT(Id) from @MyTable)
while @i < @rowsCount
begin
set @names = @names + ', ' + (select name from @MyTable where Id = @i)
set @i = @i + 1
end
select @names
此示例已使用 SQL Server 2008 R2 进行了测试。
为了避免空值,您可以使用 CONCAT()
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
评论
declare @phone varchar(max)=''
select @phone=@phone + mobileno +',' from members
select @phone
评论
Null
@phone IS Null
Null
Null
= ''
此答案需要服务器上的某些权限才能工作。
程序集对您来说是一个不错的选择。有很多网站解释了如何创建它。我认为解释得很好的就是这个。
如果你愿意,我已经创建了程序集,可以在这里下载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 函数。
评论
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
使用 COALESCE - 从这里了解更多信息
举个例子:
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
评论
Declare @Numbers AS Nvarchar(MAX)
对于其他答案,阅读答案的人必须知道特定的域表,例如车辆或学生。必须创建该表并使用数据填充该表才能测试解决方案。
下面是使用 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
SELECT PageContent = Stuff(
( SELECT PageContent
FROM dbo.InfoGuide
WHERE CategoryId = @CategoryId
AND SubCategoryId = @SubCategoryId
for xml path(''), type
).value('.[1]','nvarchar(max)'),
1, 1, '')
FROM dbo.InfoGuide info
评论
SQL Server 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
;
SELECT
sub.SubjectId
, sub.Name AS [SubjectName]
, ISNULL( x.Students, '') AS Students
FROM
dbo.Subjects sub
OUTER APPLY
(
SELECT
CASE ROW_NUMBER() OVER (ORDER BY stu.Name) WHEN 1 THEN '' ELSE ', ' END
+ stu.Name
FROM
dbo.Students stu
INNER JOIN dbo.Schedules sch
ON stu.StudentId = sch.StudentId
WHERE
sch.SubjectId = sub.SubjectId
ORDER BY
stu.Name
FOR 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 内部也使用同样的想法。 让我们希望Microsoft不要改变我。
你需要创建一个变量来保存你的最终结果,并在其中进行选择,就像这样。
最简单的解决方案
DECLARE @char VARCHAR(MAX);
SELECT @char = COALESCE(@char + ', ' + [column], [column])
FROM [table];
PRINT @char;
在 SQL Server vNext 中,这将与 STRING_AGG 函数一起内置。有关详细信息,请参阅STRING_AGG (Transact-SQL)。
SQL Server 2017+ 和 SQL Azure:STRING_AGG
从下一版本的 SQL Server 开始,我们终于可以跨行连接,而不必求助于任何变量或 XML 巫术。
不分组
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;
评论
STRING_AGG
虽然为时已晚,而且已经有很多解决方案。这是MySQL的简单解决方案:
SELECT t1.id,
GROUP_CONCAT(t1.id) ids
FROM 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 Creation
CREATE TABLE Tbl
( CustomerCode VARCHAR(50)
, CustomerName VARCHAR(50)
, Type VARCHAR(50)
,Items VARCHAR(50)
)
insert into Tbl
SELECT '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 creation
GO
CREATE FUNCTION [dbo].[fn_GetItemsByType]
(
@CustomerCode VARCHAR(50)
,@Type VARCHAR(50)
)
RETURNS @ItemType TABLE ( Items VARCHAR(5000) )
AS
BEGIN
INSERT INTO @ItemType(Items)
SELECT STUFF((SELECT distinct ',' + [Items]
FROM Tbl
WHERE CustomerCode = @CustomerCode
AND Type=@Type
FOR XML PATH(''))
,1,1,'') as Items
RETURN
END
GO
-- fianl Query
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(Type)
from Tbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT CustomerCode,CustomerName,' + @cols + '
from
(
select
distinct CustomerCode
,CustomerName
,Type
,F.Items
FROM Tbl T
CROSS APPLY [fn_GetItemsByType] (T.CustomerCode,T.Type) F
) x
pivot
(
max(Items)
for Type in (' + @cols + ')
) p '
execute(@query)
评论
使用递归查询,您可以做到这一点:
-- Create example table
CREATE TABLE tmptable (NAME VARCHAR(30)) ;
-- Insert example data
INSERT INTO tmptable VALUES('PETER');
INSERT INTO tmptable VALUES('PAUL');
INSERT INTO tmptable VALUES('MARY');
-- Recurse query
with tblwithrank as (
select * , row_number() over(order by name) rang , count(*) over() NbRow
from tmptable
),
tmpRecursive as (
select *, cast(name as varchar(2000)) as AllName from tblwithrank where rang=1
union all
select f0.*, cast(f0.name + ',' + f1.AllName as varchar(2000)) as AllName
from tblwithrank f0 inner join tmpRecursive f1 on f0.rang=f1.rang +1
)
select AllName from tmpRecursive
where rang=NbRow
我们可以按如下方式使用 RECUSRSIVITY、CTE、union 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 @mytable
union all
select 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
如果您的数据可能会重复,例如
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
这对我有用(SQL Server 2016):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
这是来源:https://www.mytecbits.com/
对于较新的 SQL 版本(最终实现)
SELECT STRING_AGG(Name, ', ') AS CarNames
FROM tbl_TypeCar;
以及MySQL的解决方案(因为此页面显示在Google中,用于MySQL):
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
来自 MySQL 文档。
首先,您应该声明一个表变量并用表数据填充它,然后使用 WHILE 循环,逐个选择行并将其值添加到 nvarchar(max) 变量中。
Go
declare @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)
begin
declare @itemTitle nvarchar(50)
set @itemTitle = (select top(1) t.Title from @temp t)
if @mainString = ''
begin
set @mainString = @itemTitle
end
else
begin
set @mainString = concat(@mainString,',',@itemTitle)
end
delete top(1) from @temp
end
print @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;
评论
在 SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 201 STRING_AGG 7SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2 请看下面的一个例子。
SELECT
VendorId, STRING_AGG(FirstName,',') UsersName
FROM Users
WHERE VendorId != 9
GROUP BY VendorId
评论
在 SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 201 STRING_AGG 7SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017下面是如何实现此目的的示例:
假设您有一个名为“Names”的表,其中“Name”列在单独的行中包含值 Peter、Paul 和 Mary,则可以使用以下 SQL 查询:
SELECT STRING_AGG(Name, ', ') AS ConcatenatedNames
FROM Names;
此查询将返回一个字符串,其名称连接起来并用逗号分隔:
ConcatenatedNames
-----------------
Peter, Paul, Mary
评论