考虑一个包含名称的数据库表,该表具有三行:
Peter
Paul
Mary
有没有简单的方法可以将其转换为单个字符串Peter, Paul, Mary
?
考虑一个包含名称的数据库表,该表具有三行:
Peter
Paul
Mary
有没有简单的方法可以将其转换为单个字符串Peter, Paul, Mary
?
Answers:
如果您使用的是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
<
或),则此方法将无效&
。参见@BenHinman的评论。
FOR XML PATH ('')
。这意味着不应将其视为可靠的,因为任何补丁程序或更新都可能改变其功能。它基本上依赖于已弃用的功能。
FOR XML
用来生成XML,而不是连接任意字符串。这就是为什么它逃脱 &
,<
并>
以XML实体代码(&
,<
,>
)。我相信它也将逃脱"
,并'
以"
和'
在属性为好。它不是 GROUP_CONCAT()
,string_agg()
,array_agg()
,listagg()
,等。即使你可以种做它做到这一点。我们应该花时间要求Microsoft实现适当的功能。
string_agg
。所有这些都可以消失。
该答案可能会返回意外结果。为获得一致的结果,请使用其他答案中详细介绍的FOR XML PATH方法之一。
用途COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
只是一些解释(因为此答案似乎获得了相对常规的意见):
1)无需@Names
使用空字符串值进行初始化。
2)无需在末端剥离额外的分隔器。
@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'替换为适合您的内容))。
从下一个版本的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;
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
+ ', '
它仍会在每个串联的元素之间添加一个空格。
SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
您可以使用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字符,这也将起作用
这'"},{"_":"'
是安全的,因为如果您包含的数据'"},{"_":"',
将被转义到"},{\"_\":\"
您可以', '
用任何字符串分隔符替换
您可以使用新的STRING_AGG函数
<
,>
,&
,等它FOR XML PATH('')
会自动逃跑。
在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
SEPARATOR '", "'
我会在最后一个条目的末尾错过一些字符。为什么会发生这种情况?
CHAR
,你需要将其强制转换,例如,通过GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2)如果你有很多价值观的到来,则应该增加group_concat_max_len
写在stackoverflow.com/a/1278210/1498405
使用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)
,效果很好。您能否解释为什么建议不使用它?
Postgres数组很棒。例:
创建一些测试数据:
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 array_to_string(array_agg(name||'('||id||')'
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
完美的作品!只需阅读此处链接的文档。wm_concat
从版本12c开始删除。
在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
<
或)时,此操作将失败&
。
我没有在家中使用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, '')
建议使用递归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
name
柱到4个以逗号分隔的字符串组的id
第 乍一看,我认为这比大多数其他SQL Server解决方案要完成的工作更多。
从PostgreSQL 9.0开始,这非常简单:
select string_agg(name, ',')
from names;
在9.0之前的版本array_agg()
中可以使用,如hgmnz所示
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
或者char
在SQL Server vNext中,它将使用STRING_AGG函数内置,在此处了解更多信息:https ://msdn.microsoft.com/zh-cn/library/mt790580.aspx
使用XML有助于我用逗号分隔行。对于多余的逗号,我们可以使用SQL Server的替换功能。不用添加逗号,而是使用AS'data()'将行与空格连接起来,以后可以用逗号替换,如下所示。
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
一个现成的解决方案,无需多余的逗号:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
空列表将导致NULL值。通常,您会将列表插入表格列或程序变量中:根据需要调整255最大长度。
(Diwakar和Jens Frandsen提供了很好的答案,但需要改进。)
', '
为','
。
对于其他答案,阅读答案的人必须知道特定的域表,例如车辆或学生。必须创建该表并在其中填充数据以测试解决方案。
下面是使用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
对于Oracle DB,请参见以下问题:如何在不创建存储过程的情况下将多行连接到Oracle中?
最好的答案似乎是@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文档提供了有关如何执行此操作的指南。
我真的很喜欢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
然后您就不必随后截断它了。
这个答案将需要服务器中的一些特权才能工作。
装配对您来说是个不错的选择。有很多网站介绍了如何创建它。我认为很好地解释了这一点
如果需要,我已经创建了程序集,可以在此处下载DLL 。
下载后,您将需要在SQL Server中运行以下脚本:
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
希望能帮助到你!!!
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
我通常使用这样的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
这对我有用(SqlServer 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
这也可能有用
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