Рассмотрим таблицу базы данных, содержащую имена с тремя строками:

Peter
Paul
Mary

Есть ли простой способ превратить это в одну строку из Peter, Paul, Mary?

2175
JohnnyM 12 Окт 2008 в 03:49
27
Для ответов, относящихся к SQL Server, попробуйте этот вопрос.
 – 
Matt Hamilton
12 Окт 2008 в 04:03
20
Для MySQL ознакомьтесь с Group_Concat из этого ответа
 – 
Pykler
6 Май 2011 в 23:48
28
Я бы хотел, чтобы следующая версия SQL Server предлагала новую функцию для элегантного решения конкатенации многострочных строк без глупости FOR XML PATH.
 – 
Pete Alvin
2 Окт 2014 в 15:47
4
Не SQL, но если это делается только один раз, вы можете вставить список в этот инструмент в браузере convert.town/column-to-comma-separated-list
 – 
Stack Man
27 Май 2015 в 10:56
3
В Oracle вы можете использовать LISTAGG (COLUMN_NAME) из 11g r2, до этого существует неподдерживаемая функция WM_CONCAT (COLUMN_NAME), которая делает то же самое.
 – 
Richard
6 Июл 2017 в 09:32

30 ответов

Лучший ответ

Если вы используете 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]

Вы можете сделать то же самое более компактным способом, если вы можете объединить запятые в начале и использовать 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
1540
Stefan Collier 2 Авг 2018 в 14:20
16
Отличное решение. Следующее может быть полезным, если вам нужно обрабатывать специальные символы, подобные тем, что в HTML: Роб Фарли: обработка специальных символов с помощью FOR XML PATH ('').
 – 
user140628
17 Апр 2013 в 16:35
11
Очевидно, это не работает, если имена содержат символы XML, такие как < или &. См. Комментарий @ BenHinman.
 – 
Sam
13 Авг 2013 в 05:26
25
NB: этот метод основан на недокументированном поведении FOR XML PATH (''). Это означает, что его нельзя считать надежным, поскольку любой патч или обновление может изменить его работу. Он в основном полагается на устаревшую функцию.
 – 
Bacon Bits
13 Ноя 2014 в 21:54
29
Суть в том, что FOR XML предназначен для генерации XML, а не для объединения произвольных строк. Вот почему он экранирует &, < и > в коды объектов XML (&amp;, &lt;, &gt;). Я предполагаю, что он также будет экранировать " и ' в &quot; и &apos; в атрибутах. Это не GROUP_CONCAT(), string_agg(), array_agg(), listagg() и т. Д., Даже если вы можете заставить его это сделать. Мы должны тратить время на то, чтобы требовать от Microsoft реализации надлежащей функции.
 – 
Bacon Bits
23 Мар 2015 в 17:15
17
Хорошие новости: MS SQL Server добавит string_agg в v.Next., и все это может исчезнуть.
 – 
Jason C
6 Апр 2017 в 03:32

Этот ответ может вернуть неожиданные результаты для согласованных результатов используйте один из методов FOR XML PATH, подробно описанных в других ответах.

Используйте COALESCE:

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

Просто некоторое объяснение (поскольку этот ответ, кажется, получает относительно регулярные просмотры):

  • Coalesce - это просто полезный чит, который выполняет две задачи:

1) Нет необходимости инициализировать @Names пустым строковым значением.

2) Нет необходимости снимать лишний разделитель на конце.

  • Приведенное выше решение даст неверные результаты, если строка имеет значение NULL Name (если есть NULL , NULL сделает @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» на то, что вам подходит]) .

1063
Martin Smith 30 Апр 2019 в 15:02
76
Чтобы было ясно, coalesce не имеет ничего общего с созданием списка, он просто гарантирует, что значения NULL не включены.
 – 
Graeme Perrow
13 Фев 2009 в 15:02
19
Perrow Он не исключает значения NULL (для этого требуется WHERE - это потеря результатов , если одно из входных значений NULL), и это требуется в этом подходе потому что: NULL + не NULL -> NULL и не NULL + NULL -> NULL; также @Name по умолчанию имеет значение NULL, и, фактически, это свойство используется здесь как неявный дозорный, чтобы определить, следует ли добавлять ',' или нет.
 – 
user166390
15 Авг 2010 в 22:57
65
Обратите внимание, что этот метод конкатенации зависит от того, что SQL Server выполняет запрос с определенным планом. Меня поймали с использованием этого метода (с добавлением ORDER BY). Когда он имел дело с небольшим количеством строк, он работал нормально, но с большим количеством данных SQL Server выбрал другой план, в результате которого был выбран первый элемент без какой-либо конкатенации. См. в этой статье Анит Сен.
 – 
fbarber
26 Апр 2012 в 06:18
17
Этот метод нельзя использовать в качестве подзапроса в списке выбора или в предложении where, поскольку он использует переменную tSQL. В таких случаях вы можете использовать методы, предлагаемые @Ritesh
 – 
R. Schreurs
2 Авг 2013 в 12:10
12
Это ненадежный метод конкатенации. Он не поддерживается и не должен использоваться (согласно Microsoft, например, support.microsoft.com/en -us / kb / 287515, connect.microsoft.com/SQLServer/Feedback / Details / 704389). Это может измениться без предупреждения. Используйте метод XML PATH, описанный в stackoverflow.com/questions/5031204/… Я написал больше здесь: marc.durdin.net/2015/07/…
 – 
Marc Durdin
15 Июл 2015 в 03:23

SQL Server 2017+ и SQL Azure: STRING_AGG

Начиная со следующей версии SQL Server, мы, наконец, можем объединять строки без необходимости прибегать к какой-либо переменной или XML-колдовству.

STRING_AGG (Transact-SQL)

Без группировки

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;
599
Community 20 Июн 2020 в 12:12
2
И, в отличие от решений CLR, вы можете контролировать сортировку.
 – 
canon
10 Июл 2017 в 19:17
Похоже, на STRING_AGG есть ограничение на отображение 4000 символов.
 – 
InspiredBy
3 Мар 2020 в 08:04
1
Есть ли способ выполнить сортировку в случае отсутствия GROUP BY (например, для примера «Без группировки»)?
 – 
RuudvK
10 Май 2020 в 12:01
Обновление: мне удалось сделать следующее, но есть ли более чистый способ? ВЫБЕРИТЕ STRING_AGG (Имя, ',') КАК ОТДЕЛЫ ИЗ (ВЫБРАТЬ ВЕРХНИЕ 100000 Имя ИЗ HumanResources. Отделение ORDER BY Name) D;
 – 
RuudvK
10 Май 2020 в 12:11
Мне пришлось применить его к NVarchar (max), чтобы он заработал ... `` SELECT STRING_AGG (CAST (EmpName as NVARCHAR (MAX)), ',') FROM EmpTable as t '' `
 – 
Varun
26 Сен 2020 в 06:25

Один метод, еще не показанный с помощью команды 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
378
user1477388 25 Апр 2016 в 18:28
16
Святое дерьмо, это потрясающе! При выполнении самостоятельно, как в вашем примере, результат форматируется как гиперссылка, при нажатии на которую (в SSMS) открывается новое окно, содержащее данные, но при использовании как части более крупного запроса оно просто отображается как строка. Это строка? или это xml, к которому мне нужно относиться по-другому в приложении, которое будет использовать эти данные?
 – 
Ben
7 Сен 2012 в 19:56
10
Этот подход также экранирует символы XML, такие как <и>. Итак, ВЫБОР '' + FName + '' приведет к " Джон Пол ..."
 – 
Lukáš Lánský
26 Фев 2014 в 22:34
8
Аккуратное решение. Я заметил, что даже когда я не добавляю + ', ', он все равно добавляет один пробел между каждым сцепленным элементом.
 – 
Baodad
4 Окт 2014 в 02:40
8
Похоже, это часть сделки. Вы можете обойти эту проблему, заменив добавленный символ токена. Например, это идеальный список с разделителями-запятыми любой длины: SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
 – 
NReilingh
29 Фев 2016 в 21:12
1
Ничего себе, на самом деле в моем тестировании с использованием data () и замены ПУТЬ более производительно, чем нет. Очень странно.
 – 
NReilingh
29 Фев 2016 в 21:33

В 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   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

Это будет работать, даже если ваши данные содержат недопустимые символы XML.

'"},{"_":"' безопасен, потому что если ваши данные содержат '"},{"_":"',, он будет экранирован в "},{\"_\":\"

Вы можете заменить ', ' любым разделителем строк


А в SQL Server 2017 База данных SQL Azure

Вы можете использовать новую функцию STRING_AGG

326
9 revs, 7 users 70% 23 Июл 2018 в 19:55
3
Хорошее использование функции STUFF для удаления первых двух символов.
 – 
David
12 Авг 2011 в 03:12
3
Мне больше всего нравится это решение, потому что я могу легко использовать его в списке выбора, добавив 'as
 – 
R. Schreurs
2 Авг 2013 в 12:27
14
Это лучше, чем принятый ответ, потому что этот параметр также обрабатывает неэкранирующие зарезервированные символы XML, такие как <, >, & и т. Д., Которые FOR XML PATH('') автоматически экранируются.
 – 
BateTech
8 Апр 2014 в 01:35

В 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
140
Pang 27 Окт 2020 в 03:37
Работает в принципе. Следует учитывать две вещи: 1) если ваш столбец не является CHAR, вам необходимо преобразовать его, например через GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',') 2) если у вас много значений, вам следует увеличить group_concat_max_len, как написано в stackoverflow.com / a / 1278210/1498405
 – 
hardmooth
14 Фев 2018 в 12:25

Используйте COALESCE - Подробнее отсюда

Например:

102

103

104

Затем напишите ниже код на сервере sql,

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
61
Graham 23 Сен 2017 в 02:27
2
Это действительно лучшее решение IMO, поскольку оно позволяет избежать проблем с кодировкой, которые представляет FOR XML. Я использовал Declare @Numbers AS Nvarchar(MAX), и он работал нормально. Не могли бы вы объяснить, почему вы не рекомендуете его использовать?
 – 
EvilDr
3 Авг 2016 в 18:01
9
Это решение было опубликовано 8 лет назад! stackoverflow.com/a/194887/986862
 – 
Andre Figueiredo
4 Май 2017 в 00:53
Почему этот запрос возвращается ??? символы вместо кириллических? Это просто проблема с выводом?
 – 
Akmal Salikhov
7 Дек 2017 в 14:31
Вы можете избежать кодирования XML. См .: stackoverflow.com/questions/15643683/…
 – 
Developer Webs
10 Мар 2021 в 22:40

Массивы 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, это стало еще проще.

51
Community 23 Май 2017 в 15:18
Если вам нужно более одного столбца, например их идентификатор сотрудника в скобках, используйте оператор concat: select array_to_string(array_agg(name||'('||id||')'
 – 
ProbablePrime
27 Фев 2015 в 14:50
Не применимо к sql-server, только к mysql
 – 
GoldBishop
4 Май 2017 в 18:03

Oracle 11g Release 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 символов.

47
Alex 14 Мар 2013 в 18:30
1
Для более старых версий Oracle идеально подходит wm_concat. Его использование объясняется в подарке по ссылке Алексом. Спасибо, Алекс!
 – 
toscanelli
20 Июл 2015 в 16:04
LISTAGG отлично работает! Просто прочтите документ, ссылка на который есть здесь. wm_concat удален из версии 12c и новее.
 – 
asgs
22 Июн 2016 в 21:56

В 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
35
George Garchagudashvili 20 Окт 2014 в 12:49
2
Я считаю, что это не работает, если значения содержат символы XML, такие как < или &.
 – 
Sam
13 Авг 2013 в 05:36

У меня нет доступа к SQL Server дома, поэтому я предполагаю здесь синтаксис, но он более или менее:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names
31
Dana 12 Окт 2008 в 04:16
12
Вам нужно будет инициализировать @names во что-то ненулевое, иначе вы получите NULL; вам также нужно будет обработать разделитель (включая ненужный)
 – 
Marc Gravell♦
12 Окт 2008 в 13:10
4
Единственная проблема с этим подходом (который я использую все время) заключается в том, что вы не можете его встроить
 – 
ekkis
24 Ноя 2012 в 02:22
2
Чтобы избавиться от начального пробела, измените запрос на SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
 – 
Tian van Heerden
4 Мар 2016 в 12:15
Кроме того, вы должны проверить, что Имя не равно нулю, вы можете сделать это, выполнив: SELECT @names = @names + ISNULL(' ' + Name, '')
 – 
Vita1ij
18 Мар 2016 в 13:49

Было предложено рекурсивное решение 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
30
MarredCheese 26 Авг 2019 в 21:22
2
Для ошеломленных: этот запрос вставляет 12 строк (3 столбца) во временную базовую таблицу, затем создает рекурсивное общее табличное выражение (rCTE), а затем сглаживает столбец name в строку, разделенную запятыми для 4 группы из id s. На первый взгляд, мне кажется, что это больше работы, чем то, что делают большинство других решений для SQL Server.
 – 
knb
24 Июл 2017 в 16:34
2
@knb: не уверен, это похвала, осуждение или просто сюрприз. Базовая таблица создана, потому что мне нравится, что мои примеры действительно работают, она не имеет ничего общего с вопросом.
 – 
jmoreno
25 Июл 2017 в 05:20

Вам нужно создать переменную, которая будет содержать ваш окончательный результат, и выбрать его, вот так.

Самое простое решение

DECLARE @char VARCHAR(MAX);

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

PRINT @char;
26
Tigerjz32 16 Ноя 2016 в 00:07

Использование XML помогло мне разделить строки запятыми. Для дополнительной запятой мы можем использовать функцию замены SQL Server. Вместо добавления запятой, использование AS 'data ()' объединит строки с пробелами, которые позже можно будет заменить запятыми в соответствии с синтаксисом, описанным ниже.

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 
18
Peter Mortensen 26 Фев 2012 в 22:42
2
На мой взгляд, это лучший ответ. Использование объявленной переменной бесполезно, когда вам нужно присоединиться к другой таблице, и это красиво и коротко. Хорошая работа.
 – 
David Roussel
2 Июн 2011 в 20:22
8
Это не работает, если в данных FName уже есть пробелы, например «Мое имя»
 – 
binball
8 Июн 2011 в 19:16
На самом деле это работает для меня на ms-sql 2016 Выберите REPLACE ((выберите Name AS 'data ()' from Brand Where Id IN (1,2,3,4) для пути xml ('')), '', ' , ') как allBrands
 – 
Rejwanul Reja
28 Апр 2017 в 13:13

Что касается других ответов, человек, читающий ответ, должен знать о конкретной таблице доменов, например о транспортном средстве или студенте. Таблица должна быть создана и заполнена данными для тестирования решения.

Ниже приведен пример, в котором используется таблица 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 
12
Mike Barlow - BarDev 4 Май 2016 в 22:31

Это сработало для меня ( SqlServer 2016 ):

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

Вот источник: https://www.mytecbits.com/ < / а>

И решение для MySql (поскольку эта страница отображается в Google для MySql)

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

Из MySql Documentations

12
Arash.Zandi 15 Янв 2020 в 12:53
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

Это помещает случайную запятую в начало.

Однако, если вам нужны другие столбцы или дочерняя таблица в CSV, вам нужно обернуть это в скалярное определяемое пользователем поле (UDF).

Вы также можете использовать путь XML в качестве коррелированного подзапроса в предложении SELECT (но мне придется подождать, пока я вернусь к работе, потому что Google не выполняет работу дома :-)

10
Peter Mortensen 26 Фев 2012 в 22:39

В ms sql Server 2017 или более поздних версиях вы можете использовать функцию STRING_AGG () для генерации значений, разделенных запятыми . Посмотрите один пример ниже. .

SELECT
VendorId,STRING_AGG(FirstName,',') UsersName FROM
Users
where VendorId!=9 GROUP BY VendorId

enter image description here

8
sameer Ahmed 4 Апр 2021 в 20:34

Для баз данных Oracle см. Этот вопрос: Как можно объединить несколько строк в одну в Oracle без создания хранимой процедуры?

Наилучшим ответом, по-видимому, является @Emmanuel, использующий встроенную функцию LISTAGG (), доступную в Oracle 11g Release 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 выше есть руководство о том, как это сделать.

7
Community 23 Май 2017 в 14:47

Чтобы избежать нулевых значений, вы можете использовать CONCAT ()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names
7
Rapunzo 12 Фев 2015 в 15:01
Было бы неплохо узнать, почему CONCAT работает. Ссылка на MSDN была бы хороша.
 – 
Reversed Engineer
20 Сен 2016 в 11:15

Пример завершения 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
7
user1767754 22 Июл 2015 в 10:51

Мне очень понравилась элегантность ответа Даны. Просто хотел закончить.

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

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

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)
7
3 revs, 2 users 92% 23 Май 2017 в 14:55
Если вы удаляете последние два символа ',', вам нужно добавить ',' после имени ('SELECT \ @names = \ @names + Name +', 'FROM Names'). Таким образом, последние два символа всегда будут ','.
 – 
JT_
18 Дек 2015 в 14:04
В моем случае мне нужно было избавиться от ведущей запятой, поэтому измените запрос на SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names, тогда вам не нужно будет усекать его впоследствии.
 – 
Tian van Heerden
4 Мар 2016 в 12:13

Этот ответ потребует некоторых привилегий на сервере для работы.

Сборки - хороший вариант для вас. Есть много сайтов, которые объясняют, как его создать. Я думаю, что это очень хорошо объяснено, это

Если хотите, я уже создал сборку, и ее можно загрузить здесь.

После того, как вы скачали его, вам нужно будет запустить следующий скрипт на вашем 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

Надеюсь, это поможет!!!


ОБНОВЛЕНИЕ:

Начиная с MS-SQL 2017, можно использовать функция STRING_AGG

6
Nizam 30 Сен 2020 в 03:49
1
Ссылка DLL содержит ошибку 404. Использование сборки для этого - излишество. См. лучший ответ для SQL Server.
 – 
Protiguous
19 Фев 2020 в 16:37

Если вы хотите иметь дело с нулями, вы можете сделать это, добавив предложение where или добавив еще один COALESCE вокруг первого.

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People
5
Pramod 28 Июл 2011 в 00:05

Поверх ответа Криса Шаффера

Если ваши данные могут повторяться, например

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
5
asmgx 16 Авг 2019 в 03:20

В Oracle это wm_concat. Я считаю, что эта функция доступна в версии 10g и выше.

4
Peter Mortensen 26 Фев 2012 в 22:44

В SQL Server vNext это будет встроено с функцией STRING_AGG, подробнее об этом читайте здесь: https://msdn.microsoft.com/en-us/library/mt790580.aspx

22
Henrik Fransas 10 Янв 2017 в 18:42

Готовое решение без лишних запятых:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

Пустой список приведет к значению NULL. Обычно вы вставляете список в столбец таблицы или программную переменную: настройте максимальную длину 255 в соответствии с вашими потребностями.

(Дивакар и Йенс Франдсен дали хорошие ответы, но нуждаются в улучшении.)

18
Peter Mortensen 27 Фев 2012 в 00:03
При использовании этого слова перед запятой ставится пробел :(
 – 
slayernoah
18 Ноя 2015 в 21:23
1
Просто замените ', ' на ',', если вам не нужно дополнительное пространство.
 – 
Daniel Reis
19 Ноя 2015 в 02:17

Обычно я использую такой выбор для объединения строк в 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
5
Vladimir Nesterovsky 6 Июл 2011 в 11:06
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
14
Max Szczurek 28 Фев 2018 в 19:04