Как объединить текст из нескольких строк в одну текстовую строку в SQL-сервере?

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

Peter
Paul
Mary

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

Чтобы получить ответы, относящиеся к SQL Server, попробуйте этот вопрос.

Matt Hamilton 12.10.2008 04:03

Для MySQL проверьте Group_Concat из этот ответ

Pykler 06.05.2011 23:48

Я бы хотел, чтобы следующая версия SQL Server предлагала новую функцию для элегантного решения конкатенации многострочных строк без глупости FOR XML PATH.

Pete Alvin 02.10.2014 15:47

пошаговое руководство для описанных выше ответов: попробуйте эту статью: [sqlmatters.com/Articles/…]

saber tabatabaee yazdi 27.12.2014 05:10

Не SQL, но если это только один раз, вы можете вставить список в этот инструмент в браузере convert.town/column-to-comma-separated-list

Stack Man 27.05.2015 10:56

В Oracle вы можете использовать LISTAGG (COLUMN_NAME) из 11g r2, до этого существует неподдерживаемая функция WM_CONCAT (COLUMN_NAME), которая делает то же самое.

Richard 06.07.2017 09:32

это решение CLR, которое можно подключить напрямую, похоже на мой sql GROUP_CONCAT, здесь

Biju jose 20.05.2018 11:22

Вы можете использовать функцию конкатенации строк. Я не могу добавить ответ (так как он заблокирован), поэтому добавляю ответ здесь: DECLARE @big_string varchar(max) = ''; SELECT @big_string += x.s + ',' FROM (VALUES ('string1'), ('string2'), ('string3')) AS x(s);. Теперь покажите результат: SELECT @big_string;. Это так просто.

JohnyL 02.08.2018 21:36
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2 047
8
2 574 129
47
Перейти к ответу Данный вопрос помечен как решенный

Ответы 47

Один из способов сделать это в 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

Работает в основном. Две вещи, которые следует учитывать: 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.02.2018 12:25

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

DECLARE @names VARCHAR(500)

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

Вам нужно будет инициализировать @names во что-то ненулевое, иначе вы получите NULL; вам также нужно будет обработать разделитель (включая ненужный)

Marc Gravell 12.10.2008 13:10

единственная проблема с этим подходом (который я использую все время) заключается в том, что вы не можете его встроить

ekkis 24.11.2012 02:22

Чтобы избавиться от начального пробела, измените запрос на SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names.

Tian van Heerden 04.03.2016 12:15

Кроме того, вы должны проверить, что Имя не равно нулю, вы можете сделать это, выполнив: SELECT @names = @names + ISNULL(' ' + Name, '')

Vita1ij 18.03.2016 13:49

This answer may return unexpected results For consistent results, use one of the FOR XML PATH methods detailed in other answers.

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

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

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

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

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

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

  • Приведенное выше решение даст неверные результаты, если строка имеет значение имени НУЛЕВОЙ (если есть НУЛЕВОЙ, НУЛЕВОЙ сделает @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

В зависимости от того, какое поведение вы хотите (первый вариант просто отфильтровывает НУЛЕВОЙ, второй вариант сохраняет их в списке с помощью маркерного сообщения [замените «N / A» на то, что вам подходит]).

Чтобы было ясно, coalesce не имеет ничего общего с созданием списка, он просто гарантирует, что значения NULL не включены.

Graeme Perrow 13.02.2009 15:02

@Graeme Perrow Он не исключает значения NULL (для этого требуется WHERE - это будет потерять результаты, если одно из входных значений NULL), и требуется в этом подходе, потому что: NULL + не-NULL -> NULL и не-NULL + NULL -> NULL; также @Name по умолчанию имеет значение NULL, и, фактически, это свойство используется здесь как неявный дозорный, чтобы определить, следует ли добавлять ',' или нет.

user166390 15.08.2010 22:57

Два способа исправить это, чтобы изящно игнорировать NULL: либо SELECT @Names = @Names + ', ' + Name FROM People WHERE Name IS NOT NULL, либо SELECT @Names = COALESCE(@Names + ', ' + Name, @Names) FROM People.

krubo 20.06.2011 05:14

@krubo Нет, проблема в том, что @Names = @Names + *anything* будет нулевым, потому что @Names имеет нулевое значение при объявлении. COALESCE разрешает и то и другое нулевое значение Nameи начальное нулевое значение @Names.

Kirk Broadhurst 25.08.2011 08:34

Это не работает для типов данных varchar и ntext, поскольку они оба несовместимы с оператором добавления.

XpiritO 28.10.2011 18:06

@XpiritO - вы имеете в виду текст и ntext? Варчар совместим; текст и ntext могут быть преобразованы (если вы используете SQL 2005, преобразуйте их в VARCHAR (MAX) / NVARCHAR (MAX), и вы ничего не потеряете; в противном случае вам все равно придется принять возможность усечения, поскольку вы можете ' t объявить переменную text / ntext).

Chris Shaffer 28.10.2011 21:11

Обратите внимание, что этот метод конкатенации зависит от того, что SQL Server выполняет запрос с определенным планом. Меня поймали с использованием этого метода (с добавлением ORDER BY). Когда он имел дело с небольшим количеством строк, он работал нормально, но с большим количеством данных SQL Server выбрал другой план, в результате которого был выбран первый элемент без какой-либо конкатенации. См. эта статья Анит Сен.

fbarber 26.04.2012 06:18

Этот метод нельзя использовать в качестве подзапроса в списке выбора или в предложении where, поскольку он использует переменную tSQL. В таких случаях вы можете использовать методы, предлагаемые @Ritesh

R. Schreurs 02.08.2013 12:10

Это решение не будет работать в представлении, в отличие от решения Ритеша.

Shinigamae 29.10.2013 00:09

Это самый простой способ создания динамического SQL, если вы хотите применить одну и ту же команду ко многим объектам. Кейд Ру использует его для переименовать схемы, а я использую его для переименовать тестовые классы tSQLt. Спасибо, Крис!

Iain Samuel McLean Elder 09.12.2013 04:33

Пробовал это, сначала понравилось, потому что это просто и блестяще. Но, как и любой другой повторяющийся процесс concat с varchar(), ввод-вывод и ЦП в конечном итоге привязываются. Для 20 000 GUID потребовалось 2 минуты, чтобы объединить их, тогда как использование for xml path('') заняло менее 1 секунды.

James L. 23.05.2014 09:16

Как добавить к нему Distinct, если я хочу добавить только отдельные значения?

confusedMind 08.08.2014 17:34

@confusedMind - вот так: DECLARE @Names VARCHAR(8000) SELECT @Names = COALESCE(@Names + ', ', '') + Name FROM ( SELECT DISTINCT Name FROM People )

Shay 12.08.2014 04:16

Это ненадежный метод конкатенации. Он не поддерживается и не должен использоваться (согласно 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.07.2015 03:23

Основная проблема этого подхода заключается в том, что он усекает все значения, превышающие 8000.

Nezam 10.08.2015 21:14

Ваше объяснение на самом деле не объясняет, что это делает. Он полагается на SQL Server, выполняющий выражение для каждой строки. Было бы здорово, если бы на это можно было положиться. Но см. Другие комментарии, указывающие на то, что SQL Server не требуется для этого типа запроса.

binki 25.06.2016 18:43

Я нашел здесь тот же самый точный пример кода (опубликованный 6 месяцев назад): codeproject.com/Tips/334400/… Я думаю, что ссылка на автора должна быть обязательной.

Leonardo Spina 30.08.2016 14:21

Штопать. Я поддержал этот ответ, но теперь, когда я знаю, что это неправильный способ выполнения конкатенации, потому что он может вас обмануть, поскольку это неподдерживаемая функция, я больше не могу удалить свой голос :-( Теперь я ввел многих в заблуждение.

Jon49 21.12.2016 23:40

Решение работает нормально; однако он обрезает текст до длины 65576

BI Dude 10.05.2017 18:32

Нельзя использовать ORDER BY для этого, не говоря уже о том, что это ужасный совет по причинам, опубликованным другими!

MC9000 07.02.2019 04:19

Спасибо за ваш вклад, это спасло мне день. Пожалуйста, дайте мне знать, сработает ли это, если строк больше сотни или больше? @ Мартин Смит

Ibrahim Inam 18.06.2020 15:16

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

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

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

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

Ответ принят как подходящий

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

Я получаю сообщение об ошибке «Неправильный синтаксис рядом с ключевым словом« Для »» при работе с MS SQL Server 2008 R2

Menefee 21.03.2012 22:21

Отличное решение. Следующее может быть полезно, если вам нужно обрабатывать специальные символы, такие как символы HTML: Роб Фарли: обработка специальных символов с помощью FOR XML PATH ('').

user140628 17.04.2013 16:35

Что делать, если нет «ID темы»

JsonStatham 26.07.2013 18:37

По-видимому, это не работает, если имена содержат символы XML, такие как < или &. См. Комментарий @ BenHinman.

Sam 13.08.2013 05:26

Это хорошее решение. Я соединил с ним 20000 GUID менее чем за 1 секунду. for xml path('') работает намного лучше, чем любой подход типа cursor и / или переменной concat.

James L. 23.05.2014 09:19

NB: этот метод основан на недокументированном поведении FOR XML PATH (''). Это означает, что его нельзя считать надежным, поскольку любой патч или обновление может изменить его работу. Он в основном полагается на устаревшую функцию.

Bacon Bits 13.11.2014 21:54

@Bacon Bits - Не могли бы вы объяснить это поподробнее? Это невероятно широко используемый фрагмент кода.

Whelkaholism 23.03.2015 13:54

@Whelkaholism Это был первый ответ Microsoft на этот запрос. Кажется, я больше не могу найти их исходное утверждение (как вы сказали, оно очень широко используется), но в течение многих лет после Server 2005 команда SQL Server утверждала, что FOR XML PATH ('') в сочетании с безымянными столбцами имеет неопределенное поведение (т. Е. Поведение не в спецификации дизайна). Даже в 2014 году вы не увидите, что FOR XML PATH ('') используется с безымянными столбцами в документации SQL Server. FOR XML PATH с безымянными колонками, да. FOR XML PATH ('') с именованными столбцами, да. Но это дает разные результаты.

Bacon Bits 23.03.2015 17:00

@Whelkaholism Суть в том, что FOR XML предназначен для генерации XML, а не для объединения произвольных строк. Вот почему он преобразует &, < и > в коды объектов XML (&amp;, &lt;, &gt;). Я предполагаю, что он также перейдет от " и ' к &quot; и &apos; в атрибутах. Это нетGROUP_CONCAT(), string_agg(), array_agg(), listagg() и т.д., даже если вы можете заставить его это сделать. Мы, должен, проводим время, требуя от Microsoft реализации надлежащей функции.

Bacon Bits 23.03.2015 17:15

Я должен использовать AS 'data ()' вместо AS [text ()].

paio 11.08.2015 16:24

@BaconBits Этот ответ использует лучший синтаксис, который лучше обрабатывает специальные символы, используя ключевое слово TYPE и извлекая содержимое с помощью функции value XML. Используя этот метод, вы даже можете использовать имена тегов и создавать правильно сформированный XML. Но не стоит бояться колонок без названий. Они официально поддерживается, по крайней мере, еще в SQL Server 2008.

Riley Major 02.10.2015 01:00

Чтобы удалить начальную запятую, я обычно использую ROW_NUMBER (). Полный образец в ответе ниже. CASE ROW_NUMBER () OVER (ORDER BY stu.Name) WHEN 1 THEN '' ELSE ',' END + stu.Name

Graeme 01.06.2016 23:39

Хорошие новости: MS SQL Server добавит string_agg в v.Next. и все это может исчезнуть.

Jason C 06.04.2017 03:32

Добавлен улучшенный пример решения проблемы, на которую указывает @Sam, символы <,> и &, возвращаемые как escape-последовательности xml & lt ;, & gt; и & amp ;.

rrozema 13.11.2017 18:29

Вы можете использовать STUFF вместо SUBSTRING, чтобы удалить начальную запятую.

Jamie Kitson 27.11.2017 14:12

Это очень плохой ответ! Он не дает простого, работоспособного примера и не объясняет, какие части необходимы для его работы. Может ли кто-нибудь, кто понимает, сделать это лучше? Может ты, @ P5Coder?

jpaugh 19.03.2018 17:13

@jpaugh почему я?

Ganesh Jadhav 19.03.2018 21:49

Я выбрал вас, потому что вы редактировали его раньше и, вероятно, понимаете это. Однако с тех пор я обнаружил, что Макс ответ выполняет эту роль, и оказывается, что используется тот же трюк SQL, хотя я сначала не распознал его из-за всего шума.

jpaugh 19.03.2018 23:41

вы можете добавить в свой пример также эту часть. Выберите Main.SubjectID, CASE WHEN Main.Students Like '%,%' THEN Left (Main.Students, Len (Main.Students) -1) ELSE Main.Students END AS [Студенты]

Maksym Sadovnychyy 29.06.2018 12:39

Вам нужно использовать GROUP BY вместо DISTINCT, это должно дать вам идентичные результаты с возможно более высокой производительностью.

Salman A 26.01.2019 21:49

используя AS [text()], сделайте поле окруженным тегом <text> ... Я просто удаляю его, и все работает (SQL Server 2008)

andynaz 01.02.2019 15:00

В 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

вы можете использовать ДЛЯ синтаксиса 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 База данных SQL Azure

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

Хорошее использование функции STUFF для удаления первых двух символов.

David 12.08.2011 03:12

Мне больше всего нравится это решение, потому что я могу легко использовать его в списке выбора, добавив 'as <label>'. Я не уверен, как это сделать с помощью решения @Ritesh.

R. Schreurs 02.08.2013 12:27

Это лучше, чем принятый ответ, потому что этот параметр также обрабатывает неэкранированные зарезервированные символы XML, такие как <, >, & и т. д., Которые FOR XML PATH('') будет автоматически экранировать.

BateTech 08.04.2014 01:35

Один метод, еще не показанный с помощью команды XMLdata() в 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

святое дерьмо, это потрясающе! При выполнении самостоятельно, как в вашем примере, результат форматируется как гиперссылка, при нажатии на которую (в SSMS) открывается новое окно, содержащее данные, но при использовании как части более крупного запроса оно просто отображается как строка. Это строка? или это xml, к которому мне нужно относиться по-другому в приложении, которое будет использовать эти данные?

Ben 07.09.2012 19:56

Этот подход также экранирует символы XML, такие как <и>. Итак, ВЫБОР '<b>' + FName + '</b>' приведет к "& lt; b & gt; John & lt; / b & gt; & lt; b & gt; Paul ..."

Lukáš Lánský 26.02.2014 22:34

Аккуратное решение. Я заметил, что даже когда я не добавляю + ', ', он все равно добавляет один пробел между каждым объединенным элементом.

Baodad 04.10.2014 02:40

Аккуратный! Есть идеи, как бороться с запятой в конце?

slayernoah 18.11.2015 04:22

@Baodad Похоже, это часть сделки. Вы можете обойти эту проблему, заменив добавленный символ токена. Например, это идеальный список с разделителями-запятыми любой длины: SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')

NReilingh 29.02.2016 21:12

Ха-ха, но в этот момент вы можете просто не использовать data () в первую очередь, а просто выполнить один из приведенных выше примеров. Кажется, что data() - это просто сокращение от «разграничить это пространство», и если вам нужно что-то еще, это бесполезно - если только это не сказывается на производительности.

NReilingh 29.02.2016 21:28

Ничего себе, на самом деле в моем тестировании с использованием data () и замены ПУТЬ более производительно, чем нет. Очень странно.

NReilingh 29.02.2016 21:33

Если вы замените data () на text (), он, похоже, сгенерирует список без необходимости обрезать пробелы.

illmortem 28.07.2017 00:00

Приятно - спасибо. Чтобы убрать запятую, я просто ставлю запятую спереди, а затем беру SUBSTRING( (SELECT ','+FName AS 'data()' FROM NameList FOR XML PATH('')),2,100000), чтобы пропустить начальную.

LoztInSpace 18.02.2020 01:46

Как бы вы это сгруппировали? скажем, у меня есть две таблицы people и roles, у человека может быть много ролей. Я хочу получить строку с разделителями-запятыми для каждой роли человека.

Spik330 17.06.2020 20:03

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

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

На мой взгляд, это лучший ответ. Использование объявленной переменной не подходит, когда вам нужно присоединиться к другой таблице, и это красиво и коротко. Хорошая работа.

David Roussel 02.06.2011 20:22

это не работает, если в данных FName уже есть пробелы, например «Мое имя»

binball 08.06.2011 19:16

На самом деле это работает для меня на ms-sql 2016 Выберите REPLACE ((выберите Name AS 'data ()' from Brand Where Id IN (1,2,3,4) для пути xml ('')), '', ' , ') как allBrands

Rejwanul Reja 28.04.2017 13:13

Как насчет этого:

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

Где «300» может быть любой ширины, учитывая максимальное количество элементов, которые, по вашему мнению, будут отображаться.

Если вам когда-нибудь придется заранее угадывать, сколько строк будет в ваших результатах, вы делаете это неправильно.

Geoff Griswald 10.01.2020 13:57

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

Обычно я использую такой выбор для объединения строк в 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

Я считаю, что это не работает, если значения содержат символы XML, такие как < или &.

Sam 13.08.2013 05:36

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

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

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

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 + Name +', 'FROM Names'). Таким образом, последние два символа всегда будут ','.

JT_ 18.12.2015 14:04

В моем случае мне нужно было избавиться от запятой ведущий, поэтому измените запрос на SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names, тогда вам не придется его усекать впоследствии.

Tian van Heerden 04.03.2016 12:13

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

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

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

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

При использовании этого слова перед запятой ставится пробел :(

slayernoah 18.11.2015 21:23

Просто замените ', ' на ',', если вам не нужно дополнительное пространство.

Daniel Reis 19.11.2015 02:17

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 символов.

Для более старых версий Oracle идеально подходит wm_concat. Его использование объясняется в подарке по ссылке Алексом. Спасибо, Алекс!

toscanelli 20.07.2015 16:04

LISTAGG работает отлично! Просто прочтите документ, ссылка на который есть здесь. wm_concat удален из версии 12c и новее.

asgs 22.06.2016 21:56

Было предложено рекурсивное решение 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

Для ошеломленных: этот запрос вставляет 12 строк (3 столбца) во временную базовую таблицу, затем создает рекурсивное общее табличное выражение (rCTE), а затем выравнивает столбец name в строку, разделенную запятыми, для 4 группыid. На первый взгляд, мне кажется, что это больше работы, чем то, что делают большинство других решений для SQL Server.

knb 24.07.2017 16:34

@knb: не уверен, это похвала, осуждение или просто сюрприз. Базовая таблица создана, потому что мне нравится, что мои примеры действительно работают, она не имеет ничего общего с вопросом.

jmoreno 25.07.2017 05:20

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

Если вам нужно более одного столбца, например, их идентификатор сотрудника в скобках, используйте оператор concat: select array_to_string(array_agg(name||'('||id||')'

ProbablePrime 27.02.2015 14:50

Не применимо к sql-сервер, только к MySQL

GoldBishop 04.05.2017 18:03

В оракуле есть несколько способов,

    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 без создания хранимой процедуры?

Лучшим ответом, по-видимому, является @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 выше есть руководство о том, как это сделать.

Это тоже может быть полезно

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

К сожалению, это поведение официально не поддерживается. MSDN говорит: «Если на переменную есть ссылка в списке выбора, ей должно быть присвоено скалярное значение, или оператор SELECT должен возвращать только одну строку». И есть люди, которые наблюдали проблемы: sqlmag.com/sql-server/multi-row-variable-assignment-and-orde‌ r

blueling 05.12.2013 13:11

Этот метод применим только к базе данных Teradata Aster, поскольку в ней используется функция NPATH.

Опять же, у нас есть таблица "Студенты".

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]

С типом ТАБЛИЦА это очень просто. Представьте, что ваша таблица называется 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

Этот пример протестирован в MS SQL Server 2008 R2.

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

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

Было бы неплохо узнать, что Почему CONCAT работает. Ссылка на MSDN была бы хороша.

Reversed Engineer 20.09.2016 11:15

   declare @phone varchar(max)='' 
   select @phone=@phone + mobileno +',' from  members
   select @phone

Почему не +', ' Как OP хотел, и вы не удаляете последний ';'. Я думаю, что этот ответ такой же, как и этот ответ;).

shA.t 20.04.2015 10:05

У меня была эта проблема, и я нашел ответ, но я хочу Concatenate с ';' поэтому я вставляю его сюда, последний элемент пуст

Hamid Bahmanabady 20.04.2015 16:24

Когда вы публикуете здесь свой ответ, он должен быть связан с вопросом, и результатом вашего кода должен быть Null, потому что вы начинаете с @phone IS Null, а добавление к Null будет Null в SQL Server, я думаю, вы забыли что-то вроде добавления = '' после вашего первого линия ;).

shA.t 20.04.2015 16:43

Нет, я отправляю ответ после проверки, и результат не был нулевым

Hamid Bahmanabady 20.04.2015 20:36

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

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

Если хотите, я уже создал сборку, и есть возможность скачать 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

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


Обновлено:

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

Ссылка DLL содержит ошибку 404. Использование сборки для этого - излишество. См. лучший ответ для SQL Server.

Protiguous 19.02.2020 16:37

Полный пример 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

Используйте КОЛЕСЦЕ - Узнайте больше здесь

Например:

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

Это действительно лучшее решение IMO, поскольку оно позволяет избежать проблем с кодировкой, которые представляет FOR XML. Я использовал Declare @Numbers AS Nvarchar(MAX), и он работал нормально. Не могли бы вы объяснить, почему вы не рекомендуете его использовать?

EvilDr 03.08.2016 18:01

Это решение было опубликовано 8 лет назад! stackoverflow.com/a/194887/986862

Andre Figueiredo 04.05.2017 00:53

Почему этот запрос возвращается ??? символы вместо кириллических? Это просто проблема с выводом?

Akmal Salikhov 07.12.2017 14:31

@EvilDr Вы можете избежать кодирования XML. См .: stackoverflow.com/questions/15643683/…

Developer Webs 10.03.2021 22:40

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

Ниже приведен пример, в котором используется таблица 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 использует ту же идею. Будем надеяться, что MS не изменит этого на мне.

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

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

DECLARE @char VARCHAR(MAX);

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

PRINT @char;

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

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;

И, в отличие от решений CLR, вы можете контролировать сортировку.

canon 10.07.2017 19:17

Похоже, на STRING_AGG есть ограничение на отображение 4000 символов.

InspiredBy 03.03.2020 08:04

Есть ли способ выполнить сортировку в случае отсутствия GROUP BY (например, для примера «Без группировки»)?

RuudvK 10.05.2020 12:01

Обновление: мне удалось сделать следующее, но есть ли более чистый способ? ВЫБРАТЬ STRING_AGG (Имя, ',') КАК ОТДЕЛЫ ИЗ (ВЫБРАТЬ ВЕРХНИЕ 100000 Имя ИЗ HumanResources. Отдел ORDER BY Name) D;

RuudvK 10.05.2020 12:11

Мне пришлось применить его к NVarchar (max), чтобы он заработал .. `` SELECT STRING_AGG (CAST (EmpName as NVARCHAR (MAX)), ',') FROM EmpTable as t '' '

Varun 26.09.2020 06:25

Хотя уже поздно, и решений уже много. Вот простое решение для MySQL:

SELECT t1.id,
        GROUP_CONCAT(t1.id) ids
 FROM table t1 JOIN table t2 ON (t1.id = t2.id)
 GROUP BY t1.id

Этот вопрос специфичен для SQL-сервера, поэтому вряд ли тот, кто в нем нуждается. Есть ли вопрос об этом же, связанный с mysql?

jpaugh 19.03.2018 16:59

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

Вопрос требует ответа, специфичного для SQL Server. Если есть вопрос PL / SQL, вы можете вместо этого ответить там. Однако сначала проверьте wm_concat и посмотрите, является ли это более простым методом.

jpaugh 19.03.2018 16:58

Вот полное решение для этого:

-- 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) 

@ User1460901 Можно попробовать примерно так:

WITH cte_base AS (
    SELECT CustomerCode, CustomerName,
    CASE WHEN Typez = 'Breakfast' THEN Items ELSE NULL END AS 'BREAKFAST'
    , CASE WHEN Typez = 'Lunch' THEN Items ELSE NULL END AS 'LUNCH'
    FROM #Customer
    )
    SELECT distinct CustomerCode, CustomerName,
    SUBSTRING(
    (   
        SELECT ','+BREAKFAST AS [text()]
        FROM cte_base b1
        WHERE b1.CustomerCode = b2.CustomerCode AND b1.CustomerName = b2.CustomerName
        ORDER BY b1.BREAKFAST
        FOR XML PATH('')
        ), 2, 1000
    ) [BREAKFAST], 
    SUBSTRING(
    (   
        SELECT ','+LUNCH AS [text()]
        FROM cte_base b1
        WHERE b1.CustomerCode = b2.CustomerCode AND b1.CustomerName = b2.CustomerName
        ORDER BY b1.LUNCH
        FOR XML PATH('')
        ), 2, 1000
    ) [LUNCH]
    FROM cte_base b2

Вы можете сделать это с помощью рекурсивного запроса:

-- 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, WITH 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

Это сработало для меня (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

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

Похоже, хороший ответ, но объясните, пожалуйста, как работает этот код.

MeanGreen 13.11.2020 15:07

Кажется, что у него будут границы очень неэффективно - как это будет работать с миллионом строк?

user2864740 30.01.2021 22:51

в Postgres - 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;

Другие вопросы по теме