Як об'єднати текст з декількох рядків у один текстовий рядок на SQL-сервері?


1911

Розглянемо таблицю бази даних, що містить імена, з трьома рядками:

Peter
Paul
Mary

Чи є простий спосіб перетворити це в єдиний рядок Peter, Paul, Mary?


26
Для відповідей, характерних для SQL Server, спробуйте це запитання .
Метт Гамільтон

17
Для MySQL перегляньте групу Group_Concat з цієї відповіді
Pykler

26
Я хотів би, щоб наступна версія SQL Server запропонувала нову функцію для елегантного вирішення багаторядкових скручування рядків без дурості ДЛЯ XML PATH.
Піт Елвін

4
Не SQL, але якщо це лише разова річ, ви можете вставити список у цей інструмент перегляду браузера convert.town/column-to-comma-separated-list
Stack Man

3
В Oracle ви можете використовувати LISTAGG (COLUMN_NAME) з 11g r2, перш ніж є непідтримувана функція WM_CONCAT (COLUMN_NAME), яка робить те саме.
Річард

Відповіді:


1432

Якщо ви перебуваєте на 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

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

10
Мабуть, це не працює, якщо імена містять символи XML, такі як <або &. Дивіться коментар @ BenHinman
Сем

23
Примітка: Цей метод залежить від незадокументованої поведінки FOR XML PATH (''). Це означає, що його не слід вважати надійним, оскільки будь-який патч або оновлення можуть змінити, як ці функції. Це в основному покладається на застарілу функцію.
Шматочки бекону

26
@Whelkaholism Суть в тому, що FOR XMLпризначена для створення XML, а не об'єднання довільних рядків. Ось чому вона вислизає &, <і >кодів XML сутностей ( &amp;, &lt;, &gt;). Я припускаю, що це також уникне "і 'в, &quot;і &apos;в атрибути також. Це НЕ GROUP_CONCAT() , string_agg(), array_agg(), listagg()і т.д. , навіть якщо ви можете частково зробити його зробити це. Ми повинні витрачати свій час на вимогу Microsoft впровадити належну функцію.
Беконові шматочки

13
Хороші новини: MS SQL Server буде string_aggдоданий у v.Next. і все це може піти.
Джейсон C

1008

Ця відповідь може повернути несподівані результати. Для послідовних результатів використовуйте один із методів FOR XML PATH, детально описаних в інших відповідях.

Використання COALESCE:

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

Лише якесь пояснення (оскільки, здається, ця відповідь має відносно регулярні погляди):

  • Coalesce - це справді просто корисна накрутка, яка виконує дві речі:

1) Не потрібно ініціалізувати @Namesзначення порожнього рядка.

2) Не потрібно знімати зайвий роздільник в кінці.

  • Розчин вище , буде видавати неправильні результати , якщо рядок має значення NULL значення Імені (якщо є 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 s, другий варіант зберігає їх у списку з маркером (замініть "N / A" на те, що вам підходить]).


72
Щоб було зрозуміло, злиття не має нічого спільного зі створенням списку, воно просто гарантує, що значення NULL не включені.
Graeme Perrow

17
@Graeme Perrow Це не виключає значень NULL (для цього потрібен WHERE - це втратить результати, якщо одне з вхідних значень NULL), і це потрібно в цьому підході, оскільки: NULL + non-NULL -> NULL і не-NULL + NULL -> NULL; Також @Name за замовчуванням NULL, і насправді це властивість використовується як неявна дозорна інформація, щоб визначити, чи слід додавати "," чи ні.

62
Зверніть увагу, що цей метод конкатенації покладається на SQL Server, виконуючи запит з певним планом. Мене спіймали за допомогою цього методу (з додаванням ЗАМОВЛЕННЯ). Коли він мав справу з невеликою кількістю рядків, він працював чудово, але з більшою кількістю даних SQL Server обрав інший план, який призвів до вибору першого пункту без зв'язання. Дивіться цю статтю Аніт Сенатор
fbarber

17
Цей метод не може бути використаний як підзапит у списку вибору або де-пункт, оскільки він використовує змінну tSQL. У таких випадках ви можете використовувати методи, запропоновані @Ritesh
R. Schreurs

11
Це не надійний метод конкатенації. Він не підтримується і не повинен використовуватися (для 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

454

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;

2
І, на відміну від рішень CLR, у вас є контроль над сортуванням.
канон

Здається, обмеження відображення в 4000 символів у STRING_AGG
InspiredBy

Чи існує спосіб сортування, якщо немає GROUP BY (так для прикладу "Без групування")?
RuudvK

Оновлення: мені вдалося зробити наступне, але чи є чистіший спосіб? ВИБІРТЕ STRING_AGG (Ім'я, ',') ЯК Відділи ВІД (ВИБІР ТОП 100000 Ім'я ВІД людських ресурсів. Відділ ЗАМОВЛЕННЯ ІМЯ) D;
RuudvK

362

Один метод, який ще не показаний через 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

15
свято s ** t дивовижно! Якщо виконано самостійно, як у вашому прикладі, результат форматується у вигляді гіперпосилання, що при натисканні на нього (у SSMS) відкриває нове вікно, що містить дані, але при використанні як частини більшого запиту він просто відображається у вигляді рядка. Це струна? чи це xml, що мені потрібно по-різному ставитися до програми, яка буде використовувати ці дані?
Бен

10
Цей підхід також уникає XML-символів типу <і>. Отже, ВИБІР "<b>" + FName + '</b> "призводить до" Іван Іоанн </B> <Пол ... "
Lukáš Lánský

8
Охайний розчин. Я помічаю, що навіть коли я не додаю його, + ', 'він все одно додає простір між кожним об'єднаним елементом.
Баодад

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

1
Нічого, насправді в моєму тестуванні з використанням даних () та заміни БУДЬ більш ефективний ніж ні. Супер дивно.
NReilingh

306

У 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 - база даних Azure SQL

Ви можете використовувати нову функцію STRING_AGG


3
Добре використовувати функцію STUFF, щоб встановити два провідні символи.
Девід

3
Це рішення мені найбільше подобається, тому що я легко можу використовувати його у списку вибору, додавши "як <мітка>". Я не впевнений, як це зробити за допомогою рішення @Ritesh.
Р. Шреурс

13
Це краще , ніж загальноприйнятому відповідь , тому що ця опція також обробляє-рятуючись символи reserverd XML , такі як <, >, &і т.д. , які FOR XML PATH('')будуть автоматично уникнути.
BateTech

Це надзвичайний відгук, оскільки він вирішив проблему та надає найкращі способи вчинити справи в різних версіях SQL, зараз я хочу, щоб я міг використовувати 2017 / Azure
Chris Ward

120

У 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 '", "'я пропущу кілька символів наприкінці останнього запису. чому це може статися?
gooleem

@gooleem Мені не зрозуміло, що ти маєш на увазі, але ця функція ставить роздільник лише між елементами, а не після. Якщо це не відповідь, рекомендую розмістити нове запитання.
Дарріл Хайн

@DarrylHein для моїх потреб я використовував роздільник, як зазначено вище. Але це скорочує мені деякі особливості в самому кінці результату. Це дуже дивно і, здається, помилка. У мене немає рішення, я просто працював навколо.
gooleem

Працює в основному. Дві речей , щоб розглянути наступні питання: 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

58

Використовуйте 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

2
Це дійсно найкраще рішення IMO, оскільки це дозволяє уникнути проблем з кодуванням, які представляє FOR XML. Я використовував, Declare @Numbers AS Nvarchar(MAX)і це працювало чудово. Чи можете ви пояснити, чому ви рекомендуєте не використовувати його будь ласка?
EvilDr

7
Це рішення вже розміщено 8 років тому! stackoverflow.com/a/194887/986862
Andre Figueiredo

Чому цей запит повертається ??? символи замість кирилиці? Це лише питання виходу?
Акмаль Саліхов

48

Масиви постгресів є приголомшливими. Приклад:

Створіть кілька тестових даних:

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||')'
Richard Fox

Не застосовується до sql-сервера , лише до mysql
GoldBishop

46

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


1
Для старих версій Oracle ідеально підходить wm_concat. Її використання пояснено у посиланні подарунка від Алекса. Думає, Алекс!
toscanelli

LISTAGGпрацює ідеально! Просто прочитайте документ, зв'язаний тут. wm_concatвидалено з версії 12c і далі.
асгс

34

У 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

2
Я вважаю, що це не вдається, коли значення містять символи XML, такі як <або &.
Сем

28

У мене вдома немає доступу до SQL-сервера, тому я думаю, що тут синтаксис, але це більш-менш:

DECLARE @names VARCHAR(500)

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

11
Вам потрібно буде надіслати @names чимось недійсним, інакше ви отримаєте NULL протягом усього часу; вам також знадобиться обробляти роздільник (включаючи непотрібний)
Marc Gravell

3
Єдина проблема з цим підходом (яким я весь час користуюся) полягає в тому, що ви не можете його вбудувати
ekkis

1
Щоб позбутися провідного місця, змініть запит наSELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
Tian van Heerden

Крім того, ви повинні перевірити, що ім'я не є нульовим, ви можете це зробити:SELECT @names = @names + ISNULL(' ' + Name, '')
Vita1ij

28

Було запропоновано рекурсивне рішення 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

1
Для ошелешив: цей запит вставки 12 рядків (в 3 колонки) в тимчасову basetable, а потім створює рекурсивную загальний стіл Вираз (РКТО) , а потім згладжує name стовпець в розділених комами рядок на 4 групи в idс. На перший погляд, я думаю, що це більше роботи, ніж більшість інших рішень для SQL Server.
кнб

2
@knb: не впевнений, це похвала, осуд чи просто сюрприз. Основна таблиця полягає в тому, що мені подобаються, що мої приклади насправді працюють, вона насправді не має нічого спільного з питанням.
jmoreno

26

Потрібно створити змінну, яка буде містити ваш кінцевий результат, і вибрати її, як це було.

Найпростіше рішення

DECLARE @char VARCHAR(MAX);

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

PRINT @char;

25

Починаючи з PostgreSQL 9.0, це досить просто:

select string_agg(name, ',') 
from names;

У версіях до 9.0 array_agg()можна використовувати, як показано hgmnz


Щоб зробити це зі стовпцями, які не є текстовим типом, вам потрібно додати SELECT string_agg(non_text_type::text, ',') FROM table
команду

@TorbenKohlmeier: це потрібно лише для стовпців без символів (наприклад, цілих чисел, десяткових). Це чудово працює для varcharабоchar
a_horse_with_no_name


18

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

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

2
Це найкраща відповідь тут, на мій погляд. Використання змінної оголошення не є корисною, коли вам потрібно приєднатись до іншої таблиці, і це добре і коротко. Гарна робота.
Девід Руссель

7
це не добре, якщо в даних FName вже є пробіли, наприклад "Моє ім'я"
binball

Дійсно, це працює для мене на ms-sql 2016 Виберіть ЗАМЕНИТИ ((виберіть Ім'я AS 'data ()' від торгової марки, де Id IN (1,2,3,4) для шляху xml ('')), '', ' , ') як allBrands
Rejwanul Reja

17

Готове до використання рішення без зайвих коми:

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

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

(Дівакар та Єнс Франдсен дали хороші відповіді, але потребують вдосконалення.)


Перед комою є пробіл при використанні цього :(
slayernoah

1
Просто замініть ', 'з , ','якщо ви не хочете , щоб додатковий простір.
Даніель Рейс

13
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

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

Це ставить бродячу кому на початку.

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

Ви можете використовувати шлях XML як співвіднесений підзапит у пункті SELECT (але мені доведеться почекати, поки я повернусь до роботи, тому що Google не працює вдома :-)


10

З іншими відповідями особа, що читає відповідь, повинна знати про конкретну таблицю домену, наприклад транспортний засіб або студент. Для перевірки рішення необхідно створити таблицю та заповнити її даними.

Нижче наводиться приклад, який використовує таблицю "Інформація_Схема.Колони" SQL Server ". Використовуючи це рішення, не потрібно створювати таблиці або додавати дані. Цей приклад створює розділений комами список імен стовпців для всіх таблиць бази даних.

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 

7

Для баз даних 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

Щоб уникнути нульових значень, ви можете використовувати CONCAT ()

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

Було б непогано знати, чому CONCAT працює. Посилання на MSDN було б непогано.
Реверсивний інженер

7

Мені дуже сподобалася елегантність відповіді Дани . Просто хотілося зробити його завершеним.

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_

У моєму випадку мені потрібно було позбутися провідної коми, тому змініть запит, щоб SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Namesпотім не потрібно усікати його.
Тіан ван Херден

6

Ця відповідь потребує певної привілеї для роботи сервера.

Збори - це хороший варіант для вас. Існує маса сайтів, які пояснюють, як її створити. Один я думаю , що дуже добре пояснив це один

Якщо ви хочете, я вже створив збірку, і завантажити DLL можна тут .

Завантаживши його, вам потрібно буде запустити такий сценарій у своєму SQL сервері:

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

Сподіваюся, це допомагає !!!


1
Посилання DLL - це помилка 404. Використання збірки для цього є надмірним. Дивіться найкращу відповідь для SQL Server.
Попередній

6

Приклад завершеного 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

5

Я зазвичай використовую 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

5

Якщо ви хочете мати справу з нулями, ви можете зробити це, додавши пункт де або додавши інший COALESCE навколо першого.

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

5

Це працювало для мене ( 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



4

Це теж може бути корисно

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

5
На жаль, ця поведінка офіційно не підтримується. MSDN каже: "Якщо на змінну посилається у списку вибору, їй слід призначити скалярне значення або оператор SELECT повинен повернути лише один рядок." І є люди, які спостерігали проблеми: sqlmag.com/sql-server/multi-row-variable-assignment-and-order
затуплення
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.