30 ответов:
если вы находитесь на SQL Server 2017 или Azure, см. ответ Матье Ренда.
у меня была аналогичная проблема, когда я пытался объединить две таблицы с отношениями "один ко многим". В SQL 2005 я обнаружил, что
XML PATHметод может обрабатывать конкатенацию строк очень легко.если есть таблица под названием
STUDENTSSubjectID 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
этот ответ может возвращать неожиданные результаты, когда присутствует предложение ORDER BY. Для получения согласованных результатов используйте один из методов for XML PATH, описанных в других ответах.
использовать
COALESCE:DECLARE @Names VARCHAR(8000) SELECT @Names = COALESCE(@Names + ', ', '') + Name FROM Peopleпросто некоторое объяснение (поскольку этот ответ, похоже, получает относительно регулярные представления):
- Coalesce на самом деле просто полезный чит, который выполняет две вещи:
1) Нет необходимости инициализировать
@NamesС пустой строкой.2) отсутствие потребности обнажать дополнительный разделитель в конце.
- решение выше даст неправильные результаты, если строка имеет NULL Значение Имени (если есть NULL на NULL сделает
@NamesNULL после этой строки, и следующая строка снова начнется как пустая строка. Легко исправить одним из двух решения: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в зависимости от того, какое поведение вы хотите (первый вариант просто фильтров NULLs out, второй вариант сохраняет их в списке с сообщением маркера [заменить 'N / A' с тем, что подходит для вас]).
один метод еще не показан через
XMLdata()команда в MS SQL Server:предположим, что таблица называется NameList с одним столбцом под названием FName,
SELECT FName + ', ' AS 'data()' FROM NameList FOR XML PATH('')возвращает:
"Peter, Paul, Mary, "только лишняя запятая должна быть обработана.
Edit: как принято из комментария @NReilingh, вы можете использовать следующий метод для удаления конечной запятой. Предполагая те же имена таблиц и столбцов:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
на 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
the
'"},{"_":"'безопасно, потому что если Вы данные содержат'"},{"_":"',он будет экранирован в"},{\"_\":\"вы можете заменить
', 'с любым разделителем строк
и в SQL Server 2017, база данных SQL Azure
можно использовать STRING_AGG функция
SQL Server 2017+ и SQL Azure: STRING_AGG
начиная со следующей версии SQL Server, мы можем, наконец, объединить все строки, не прибегая к какой-либо переменной или XML-ведьмовству.
без группировки
SELECT STRING_AGG(Name, ', ') AS Departments FROM HumanResources.Department;С группировкой :
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments FROM HumanResources.Department GROUP BY GroupName;С группировкой и подсортировкой
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments FROM HumanResources.Department GROUP BY GroupName;
в 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
использовать сливаются -подробнее здесь
для примера:
102
103
104
затем напишите ниже код в sql server,
Declare @Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers SELECT @Numbers = COALESCE(@Numbers + ',', '') + Number FROM TableName where Number IS NOT NULL SELECT @Numbersвывод:
102,103,104
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 символов.
массивы 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 это еще проще.
в SQL Server 2005 и более поздних версиях используйте запрос ниже для объединения строк.
DECLARE @t table ( Id int, Name varchar(10) ) INSERT INTO @t SELECT 1,'a' UNION ALL SELECT 1,'b' UNION ALL SELECT 2,'c' UNION ALL SELECT 2,'d' SELECT ID, stuff( ( SELECT ','+ [Name] FROM @t WHERE Id = t.Id FOR XML PATH('') ),1,1,'') FROM (SELECT DISTINCT ID FROM @t ) t
У меня нет доступа к SQL-серверу дома, поэтому я думаю о синтаксисе здесь, но это более или менее:
DECLARE @names VARCHAR(500) SELECT @names = @names + ' ' + Name FROM Names
было предложено рекурсивное решение 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
начиная с PostgreSQL 9.0 это довольно просто:
select string_agg(name, ',') from names;в версиях до 9.0
array_agg()может использоваться как показано hgmnz
вам нужно создать переменную, которая будет содержать ваш конечный результат и выбрать в него, например.
Простое Решение
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
использование XML помогло мне в получении строк, разделенных запятыми. Для дополнительной запятой мы можем использовать функцию замены SQL Server. Вместо добавления запятой, использование AS 'data ()' объединит строки с пробелами, которые позже могут быть заменены запятыми, как синтаксис, написанный ниже.
REPLACE( (select FName AS 'data()' from NameList for xml path('')) , ' ', ', ')
готовое к использованию решение, без лишних запятых:
select substring( (select ', '+Name AS 'data()' from Names for xml path('')) ,3, 255) as "MyList"пустой список приведет к нулевому значению. Обычно вы вставляете список в столбец таблицы или программную переменную: отрегулируйте максимальную длину 255 в соответствии с вашими потребностями.
(Дивакар и Йенс Франдсен предоставили хорошие ответы, но нуждаются в улучшении.)
DECLARE @Names VARCHAR(8000) SELECT @name = '' SELECT @Names = @Names + ',' + Names FROM People SELECT SUBSTRING(2, @Names, 7998)это ставит случайную запятую в начале.
однако, если вам нужны другие столбцы или CSV дочерняя таблица, вам нужно обернуть ее в скалярное пользовательское поле (UDF).
вы также можете использовать XML-путь в качестве коррелированного подзапроса в предложении SELECT (но мне придется подождать, пока я вернусь к работе, потому что Google не работает дома: -)
С другими ответами человек, читающий ответ, должен знать о конкретной таблице домена, такой как автомобиль или студент. Для тестирования решения необходимо создать таблицу и заполнить ее данными.
Ниже приведен пример, который использует SQL Server " Information_Schema.Столбцы " таблица. С помощью этого решения не требуется создавать таблицы или добавлять данные. В этом примере создается разделенный запятыми список имен столбцов для всех таблиц в базе данных.
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 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
для Oracle DBs см. Этот вопрос:Как можно объединить несколько строк в одну в 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 выше есть руководство о том, как это сделать.
чтобы избежать нулевых значений, вы можете использовать CONCAT ()
DECLARE @names VARCHAR(500) SELECT @names = CONCAT(@names, ' ', name) FROM Names select @names
этот ответ потребует некоторых привилегий в сервере для работы.
сборки являются хорошим вариантом для вас. Есть много сайтов, которые объясняют, как создать его. Тот, который я думаю, очень хорошо объяснил это один
Если хотите, я уже создал сборку, и можно скачать 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надеюсь, что это помогает!!!
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
Мне очень понравилась элегантность Дане. Просто хотел сделать его полным.
DECLARE @names VARCHAR(MAX) SET @names = '' SELECT @names = @names + ', ' + Name FROM Names -- Deleting last two symbols (', ') SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)
Я обычно использую 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
это тоже может быть полезно
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
Если вы хотите иметь дело с нулями, вы можете сделать это, добавив предложение where или добавив другое объединение вокруг первого.
DECLARE @Names VARCHAR(8000) SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People
в Oracle, это
wm_concat. Я считаю, что эта функция доступна в 10г выпуска и выше.
-- 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) ;
Comments