Преобразование нескольких строк в одну с запятой в качестве разделителя

Если я выпущу SELECT username FROM Users Я получаю этот результат:

имя пользователя
--------
Павел
Джон
Мэри

но что мне действительно нужно, так это одна строка со всеми значениями, разделенными запятой, например:

Пол, Джон, Мэри

Как мне это сделать?

10 ответов

Решение

Это должно работать для вас. Протестировано вплоть до SQL 2000.

create table #user (username varchar(25))

insert into #user (username) values ('Paul')
insert into #user (username) values ('John')
insert into #user (username) values ('Mary')

declare @tmp varchar(250)
SET @tmp = ''
select @tmp = @tmp + username + ', ' from #user

select SUBSTRING(@tmp, 0, LEN(@tmp))
 select
   distinct  
    stuff((
        select ',' + u.username
        from users u
        where u.username = username
        order by u.username
        for xml path('')
    ),1,1,'') as userlist
from users
group by username

раньше была опечатка, вышеперечисленные работы

Хороший обзор нескольких подходов:

http://blogs.msmvps.com/robfarley/2007/04/07/coalesce-is-not-the-answer-to-string-concatentation-in-t-sql/

Копия статьи -

Coalesce не является ответом на конкатенацию строк в T-SQL. За последние годы я видел много публикаций об использовании функции COALESCE для работы конкатенации строк в T-SQL. Это один из примеров здесь (заимствовано у Readifarian Marc Ridey).

DECLARE @categories varchar(200)
SET @categories = NULL

SELECT @categories = COALESCE(@categories + ',','') + Name
FROM Production.ProductCategory

SELECT @categories

Этот запрос может быть достаточно эффективным, но необходимо соблюдать осторожность и правильно понимать использование COALESCE. COALESCE - это версия ISNULL, которая может принимать более двух параметров. Возвращает первое в списке параметров, которое не является нулевым. Так что на самом деле это не имеет ничего общего с конкатенацией, и следующий фрагмент кода точно такой же - без использования COALESCE:

DECLARE @categories varchar(200)
SET @categories = ''

SELECT @categories = @categories + ',' + Name
FROM Production.ProductCategory

SELECT @categories

Но неупорядоченный характер баз данных делает это ненадежным. Вся причина, по которой у T-SQL (пока) нет функции сцепления, заключается в том, что это агрегат, для которого важен порядок элементов. Используя этот метод присвоения переменных конкатенации строк, вы можете обнаружить, что возвращаемый ответ не имеет всех значений, особенно если вы хотите, чтобы подстроки были расположены в определенном порядке. Рассмотрим следующее, которое на моей машине возвращает только "Аксессуары", когда я хотел, чтобы оно возвращалось "Велосипеды, Одежда, Компоненты, Аксессуары":

DECLARE @categories varchar(200)
SET @categories = NULL

SELECT @categories = COALESCE(@categories + ',','') + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)

SELECT @categories

Гораздо лучше использовать метод, который учитывает порядок и который был включен в SQL2005 специально для объединения строк - FOR XML PATH('')

SELECT ',' + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
FOR XML PATH('') 

В публикации, в которой я недавно сравнивал GROUP BY и DISTINCT при использовании подзапросов, я продемонстрировал использование FOR XML PATH(''). Посмотрите на это, и вы увидите, как это работает в подзапросе. Функция "STUFF" предназначена только для удаления запятой.

USE tempdb;
GO
CREATE TABLE t1 (id INT, NAME VARCHAR(MAX));
INSERT t1 values (1,'Jamie');
INSERT t1 values (1,'Joe');
INSERT t1 values (1,'John');
INSERT t1 values (2,'Sai');
INSERT t1 values (2,'Sam');
GO

select
    id,
    stuff((
        select ',' + t.[name]
        from t1 t
        where t.id = t1.id
        order by t.[name]
        for xml path('')
    ),1,1,'') as name_csv
from t1
group by id
; 

FOR XML PATH - это единственная ситуация, в которой вы можете использовать ORDER BY в подзапросе. Другой ТОП. И когда вы используете безымянный столбец и FOR XML PATH(''), вы получите прямую конкатенацию без тегов XML. Это означает, что строки будут закодированы в HTML, поэтому, если вы объединяете строки, которые могут иметь символ <(и т. Д.), То вам, возможно, следует исправить это позже, но в любом случае, это по-прежнему лучший способ объединения строк. в SQL Server 2005

Опираясь на Мвигдалс ответ. если вам также нужно сделать группировку здесь, как заставить ее выглядеть

group, csv
'group1', 'paul, john'
'group2', 'mary'

    --drop table #user
create table #user (groupName varchar(25), username varchar(25))

insert into #user (groupname, username) values ('apostles', 'Paul')
insert into #user (groupname, username) values ('apostles', 'John')
insert into #user (groupname, username) values ('family','Mary')


select
    g1.groupname
    , stuff((
        select ', ' + g.username
        from #user g        
        where g.groupName = g1.groupname        
        order by g.username
        for xml path('')
    ),1,2,'') as name_csv
from #user g1
group by g1.groupname

Вы можете использовать этот запрос для выполнения вышеуказанной задачи:

DECLARE @test NVARCHAR(max)  
SELECT @test = COALESCE(@test + ',', '') + field2 FROM #test
SELECT field2 = @test 

Для получения подробной информации и пошагового объяснения посетите следующую ссылку http://oops-solution.blogspot.com/2011/11/sql-server-convert-table-column-data.html

DECLARE @EmployeeList varchar(100)

SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') + 
   CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1

SELECT @EmployeeList

источник: http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string

В SQLite это проще. Я думаю, что есть аналогичные реализации для MySQL, MSSql и Orable

CREATE TABLE Beatles (id integer, name string );
INSERT INTO Beatles VALUES (1, "Paul");
INSERT INTO Beatles VALUES (2, "John");
INSERT INTO Beatles VALUES (3, "Ringo");
INSERT INTO Beatles VALUES (4, "George");
SELECT GROUP_CONCAT(name, ',') FROM Beatles;

Вы можете использовать stuff() для преобразования строк в значения, разделенные запятыми

select
EmployeeID,
stuff((
  SELECT ',' + FPProjectMaster.GroupName 
      FROM     FPProjectInfo AS t INNER JOIN
              FPProjectMaster ON t.ProjectID = FPProjectMaster.ProjectID
      WHERE  (t.EmployeeID = FPProjectInfo.EmployeeID)
              And t.STatusID = 1
              ORDER BY t.ProjectID
       for xml path('')
       ),1,1,'') as name_csv
from FPProjectInfo
group by EmployeeID;

Спасибо @AlexKuznetsov за ссылку, чтобы получить этот ответ.

Чистое и гибкое решение в MS SQL Server 2005/2008 заключается в создании функции CLR Agregate.

Вы найдете довольно много статей (с кодом) в Google.

Похоже, эта статья проведет вас через весь процесс с использованием C#.

Если вы выполняете это через PHP, что по этому поводу?

$hQuery = mysql_query("SELECT * FROM users");
while($hRow = mysql_fetch_array($hQuery)) {
    $hOut .= $hRow['username'] . ", ";
}
$hOut = substr($hOut, 0, strlen($hOut) - 1);
echo $hOut;
Другие вопросы по тегам