Pertimbangkan tabel database yang menyimpan nama, dengan tiga baris:
Peter
Paul
Mary
Apakah ada cara mudah untuk mengubahnya menjadi satu string Peter, Paul, Mary
?
Pertimbangkan tabel database yang menyimpan nama, dengan tiga baris:
Peter
Paul
Mary
Apakah ada cara mudah untuk mengubahnya menjadi satu string Peter, Paul, Mary
?
Jawaban:
Jika Anda menggunakan SQL Server 2017 atau Azure, lihat jawaban Mathieu Renda .
Saya memiliki masalah yang sama ketika saya mencoba untuk bergabung dengan dua tabel dengan hubungan satu ke banyak. Dalam SQL 2005 saya menemukan bahwa XML PATH
metode dapat menangani penggabungan baris dengan sangat mudah.
Jika ada tabel yang disebut STUDENTS
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
Hasil yang saya harapkan adalah:
SubjectID StudentName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
Saya menggunakan yang berikut ini 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]
Anda dapat melakukan hal yang sama dengan cara yang lebih kompak jika Anda dapat menyatukan koma di awal dan menggunakan substring
untuk melewati yang pertama sehingga Anda tidak perlu melakukan sub-permintaan:
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
<
atau &
. Lihat @ BenHinman komentar.
FOR XML PATH ('')
. Itu berarti itu tidak dapat dianggap andal karena setiap patch atau pembaruan dapat mengubah cara kerjanya. Ini pada dasarnya mengandalkan fitur yang sudah usang.
FOR XML
ini dimaksudkan untuk menghasilkan XML, bukan string acak. Itulah mengapa lolos &
, <
dan >
untuk kode XML entitas ( &
, <
, >
). Saya menganggap itu juga akan melarikan diri "
dan '
ke "
dan '
dalam atribut juga. Ini tidak GROUP_CONCAT()
, string_agg()
, array_agg()
, listagg()
, dll bahkan jika Anda dapat jenis make melakukan itu. Kita harus menghabiskan waktu kita menuntut Microsoft menerapkan fungsi yang tepat.
string_agg
di v.Next. dan semua ini bisa hilang.
Jawaban ini dapat mengembalikan hasil yang tidak diharapkan. Untuk hasil yang konsisten, gunakan salah satu metode UNTUK XML PATH yang dirinci dalam jawaban lain.
Gunakan COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
Hanya beberapa penjelasan (karena jawaban ini tampaknya mendapatkan pandangan yang relatif teratur):
1) Tidak perlu menginisialisasi @Names
dengan nilai string kosong.
2) Tidak perlu melepas pemisah tambahan di akhir.
@Names
NULL setelah baris itu, dan baris berikutnya akan memulai kembali sebagai string kosong lagi. Mudah diperbaiki dengan satu dari dua solusi:DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL
atau:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') +
ISNULL(Name, 'N/A')
FROM People
Bergantung pada perilaku apa yang Anda inginkan (opsi pertama hanya menyaring NULL keluar, opsi kedua menyimpannya dalam daftar dengan pesan penanda [ganti 'N / A' dengan apa pun yang sesuai untuk Anda]).
Dimulai dengan versi SQL Server berikutnya, kita akhirnya bisa menyatukan seluruh baris tanpa harus menggunakan variabel apa pun atau sihir XML.
Tanpa pengelompokan
SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;
Dengan pengelompokan:
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Dengan pengelompokan dan sub-sortasi
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Salah satu metode yang belum ditampilkan melalui XML
data()
perintah di MS SQL Server adalah:
Asumsikan tabel bernama NameList dengan satu kolom bernama FName,
SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')
pengembalian:
"Peter, Paul, Mary, "
Hanya koma ekstra yang harus ditangani.
Sunting: Seperti yang diadopsi dari komentar @ NReilingh, Anda dapat menggunakan metode berikut untuk menghapus koma tertinggal. Dengan asumsi nama tabel dan kolom yang sama:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
+ ', '
masih menambahkan satu ruang di antara setiap elemen gabungan.
SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
Anda dapat menggunakan sintaks FOR JSON
yaitu
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
Dan hasilnya akan menjadi
Id Emails
1 abc@gmail.com
2 NULL
3 def@gmail.com, xyz@gmail.com
Ini akan berfungsi bahkan data Anda mengandung karakter XML yang tidak valid
yang '"},{"_":"'
aman karena jika data yang mengandung '"},{"_":"',
itu akan melarikan diri ke"},{\"_\":\"
Anda dapat mengganti ', '
dengan pemisah string apa pun
Anda dapat menggunakan fungsi STRING_AGG baru
<
, >
, &
, dll, yang FOR XML PATH('')
secara otomatis akan melarikan diri.
Di MySQL ada fungsi, GROUP_CONCAT () , yang memungkinkan Anda menggabungkan nilai dari beberapa baris. Contoh:
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 '", "'
saya akan kehilangan beberapa karakter di akhir entri terakhir. mengapa ini bisa terjadi?
CHAR
, Anda harus melemparkannya, misalnya melalui GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2) jika Anda memiliki banyak nilai yang datang, Anda harus meningkatkan group_concat_max_len
seperti yang tertulis di stackoverflow.com/a/1278210/1498405
Gunakan COALESCE - Pelajari lebih lanjut dari sini
Sebagai contoh:
102
103
104
Kemudian tulis kode di bawah ini di 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
Outputnya adalah:
102,103,104
Declare @Numbers AS Nvarchar(MAX)
dan itu bekerja dengan baik. Bisakah Anda jelaskan mengapa Anda merekomendasikan untuk tidak menggunakannya?
Array postgres mengagumkan. Contoh:
Buat beberapa data uji:
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)
Gabungkan mereka dalam sebuah array:
test=# select array_agg(name) from names;
array_agg
-------------------
{Peter,Paul,Mary}
(1 row)
Konversi array menjadi string yang dibatasi koma:
test=# select array_to_string(array_agg(name), ', ') from names;
array_to_string
-------------------
Peter, Paul, Mary
(1 row)
DIBUAT
Sejak PostgreSQL 9.0 bahkan lebih mudah .
select array_to_string(array_agg(name||'('||id||')'
Oracle 11g Release 2 mendukung fungsi LISTAGG. Dokumentasi di sini .
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.
Hati-hati menerapkan fungsi ini jika ada kemungkinan string yang dihasilkan melebihi 4000 karakter. Itu akan melempar pengecualian. Jika itu yang terjadi maka Anda perlu menangani pengecualian atau memutar fungsi Anda sendiri yang mencegah string yang tergabung untuk lebih dari 4000 karakter.
LISTAGG
bekerja dengan sempurna! Baca saja dokumen yang tertaut di sini. wm_concat
dihapus dari versi 12c dan seterusnya.
Di SQL Server 2005 dan yang lebih baru, gunakan kueri di bawah ini untuk menyatukan baris.
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
<
atau &
.
Saya tidak memiliki akses ke SQL Server di rumah, jadi saya kira sintaks di sini, tetapi lebih atau kurang:
DECLARE @names VARCHAR(500)
SELECT @names = @names + ' ' + Name
FROM Names
SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
SELECT @names = @names + ISNULL(' ' + Name, '')
Solusi CTE rekursif disarankan, tetapi tidak ada kode yang diberikan. Kode di bawah ini adalah contoh CTE rekursif. Perhatikan bahwa meskipun hasilnya cocok dengan pertanyaan, data tidak cukup cocok dengan deskripsi yang diberikan, karena saya berasumsi bahwa Anda benar-benar ingin melakukan ini pada grup baris, tidak semua baris dalam tabel. Mengubahnya untuk mencocokkan semua baris dalam tabel dibiarkan sebagai latihan untuk pembaca.
;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
name
kolom ke dalam string dipisahkan koma selama 4 kelompok dari id
s. Pada pandangan pertama, saya pikir ini lebih berfungsi daripada apa yang dilakukan kebanyakan solusi lain untuk SQL Server.
Dimulai dengan PostgreSQL 9.0 ini cukup sederhana:
select string_agg(name, ',')
from names;
Dalam versi sebelum 9.0 array_agg()
dapat digunakan seperti yang ditunjukkan oleh hgmnz
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
atauchar
Dalam SQL Server vNext ini akan dibangun dengan fungsi STRING_AGG, baca lebih lanjut di sini: https://msdn.microsoft.com/en-us/library/mt790580.aspx
Menggunakan XML membantu saya memisahkan baris dengan koma. Untuk koma ekstra kita dapat menggunakan fungsi ganti dari SQL Server. Alih-alih menambahkan koma, penggunaan AS 'data ()' akan menyatukan baris dengan spasi, yang nantinya dapat diganti dengan koma seperti sintaks yang ditulis di bawah ini.
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
Solusi siap pakai, tanpa koma tambahan:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
Daftar kosong akan menghasilkan nilai NULL. Biasanya Anda akan memasukkan daftar ke dalam kolom tabel atau variabel program: sesuaikan panjang maks 255 dengan kebutuhan Anda.
(Diwakar dan Jens Frandsen memberikan jawaban yang baik, tetapi perlu perbaikan.)
', '
dengan ','
jika Anda tidak menginginkan ruang ekstra.
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')
Berikut ini contohnya:
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
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)
Ini menempatkan koma liar di awal.
Namun, jika Anda membutuhkan kolom lain, atau CSV tabel anak, Anda harus membungkusnya dalam bidang yang ditentukan pengguna skalar (UDF).
Anda dapat menggunakan jalur XML sebagai subquery berkorelasi dalam klausa SELECT juga (tapi saya harus menunggu sampai saya kembali bekerja karena Google tidak melakukan pekerjaan di rumah :-)
Dengan jawaban lain, orang yang membaca jawabannya harus mengetahui tabel domain tertentu seperti kendaraan atau siswa. Tabel harus dibuat dan diisi dengan data untuk menguji solusi.
Di bawah ini adalah contoh yang menggunakan tabel SQL Server "Information_Schema.Columns". Dengan menggunakan solusi ini, tidak perlu membuat tabel atau data ditambahkan. Contoh ini membuat daftar nama kolom yang dipisahkan koma untuk semua tabel dalam database.
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
Untuk Oracle DB, lihat pertanyaan ini: Bagaimana beberapa baris dapat digabungkan menjadi satu di Oracle tanpa membuat prosedur tersimpan?
Jawaban terbaik tampaknya oleh @Emmanuel, menggunakan fungsi LISTAGG () bawaan, tersedia di Oracle 11g Release 2 dan yang lebih baru.
SELECT question_id,
LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
seperti yang ditunjukkan oleh @ user762952, dan menurut dokumentasi Oracle http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , fungsi WM_CONCAT () juga merupakan opsi. Tampaknya stabil, tetapi Oracle secara eksplisit merekomendasikan untuk tidak menggunakannya untuk aplikasi SQL apa pun, jadi gunakan dengan risiko Anda sendiri.
Selain itu, Anda harus menulis fungsi Anda sendiri; dokumen Oracle di atas memiliki panduan tentang cara melakukan itu.
Untuk menghindari nilai nol, Anda dapat menggunakan CONCAT ()
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
Saya sangat menyukai keanggunan jawaban Dana . Hanya ingin membuatnya lengkap.
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 + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names
kemudian Anda tidak harus memotongnya setelah itu.
Jawaban ini akan membutuhkan beberapa hak istimewa di server untuk bekerja.
Assemblies adalah pilihan yang bagus untuk Anda. Ada banyak situs yang menjelaskan cara membuatnya. Yang saya pikir sangat baik dijelaskan apakah ini salah satu
Jika Anda mau, saya sudah membuat perakitan, dan dimungkinkan untuk mengunduh DLL di sini .
Setelah Anda mengunduhnya, Anda harus menjalankan skrip berikut di SQL Server Anda:
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
Perhatikan bahwa jalur menuju perakitan mungkin dapat diakses ke server. Karena Anda telah berhasil melakukan semua langkah, Anda dapat menggunakan fungsi seperti:
SELECT dbo.Concat(field1, ',')
FROM Table1
Semoga bisa membantu !!!
Contoh lengkap MySQL:
Kami memiliki Pengguna yang dapat memiliki banyak Data dan kami ingin memiliki keluaran, di mana kami dapat melihat semua data pengguna dalam daftar:
Hasil:
___________________________
| id | rowList |
|-------------------------|
| 0 | 6, 9 |
| 1 | 1,2,3,4,5,7,8,1 |
|_________________________|
Pengaturan meja:
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);
Pertanyaan:
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
Saya biasanya menggunakan pilih seperti ini untuk menggabungkan string di 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
Ini bekerja untuk saya ( SqlServer 2016 ):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
Inilah sumbernya: https://www.mytecbits.com/
Dan solusi untuk MySql (karena halaman ini muncul di Google untuk MySql)
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
Dari Dokumentasi MySql
Di Oracle, itu wm_concat
. Saya percaya fungsi ini tersedia dalam rilis 10g dan lebih tinggi.
Ini juga bisa bermanfaat
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
kembali
Peter,Paul,Mary