Üç satırlı adları tutan bir veritabanı tablosu düşünün:
Peter
Paul
Mary
Bunu tek bir dizeye dönüştürmenin kolay bir yolu var mı Peter, Paul, Mary
?
Üç satırlı adları tutan bir veritabanı tablosu düşünün:
Peter
Paul
Mary
Bunu tek bir dizeye dönüştürmenin kolay bir yolu var mı Peter, Paul, Mary
?
Yanıtlar:
SQL Server 2017 veya Azure kullanıyorsanız, Mathieu Renda yanıtına bakın .
Bir-çok ilişkileri olan iki tabloya katılmaya çalışırken benzer bir sorun yaşadım. SQL 2005'te bu XML PATH
yöntemin satırların birleşimini çok kolay bir şekilde ele alabileceğini buldum .
Adlı bir tablo varsa STUDENTS
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
Beklediğim sonuç:
SubjectID StudentName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
Aşağıdakileri kullandım 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]
Virgülleri başlangıçta substring
bitirebilir ve ilkini atlamak için kullanabilirsiniz , böylece bir alt sorgu yapmanız gerekmez: Aynı şeyi daha kompakt bir şekilde yapabilirsiniz :
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
<
veya gibi XML karakterleri içeriyorsa bu işe yaramaz &
. @ BenHinman'ın yorumuna bakın.
FOR XML PATH ('')
. Bu, herhangi bir yama veya güncellemenin bu işlevini değiştirebileceğinden güvenilir olarak düşünülmemesi gerektiği anlamına gelir. Temel olarak kullanımdan kaldırılmış bir özelliğe dayanıyor.
FOR XML
, rastgele dizeleri birleştirmek yerine XML üretmeyi amaçlamaktadır. İşte bu kaçar yüzden &
, <
ve >
XML varlığı kodlarına ( &
, <
, >
). Ben de kaçmak varsayıyorum "
ve '
hiç "
ve '
özelliklerinde de. O oluyor değil GROUP_CONCAT()
, string_agg()
, array_agg()
, listagg()
, vb tür make it bunu yapabilir bile. Biz gereken Microsoft düzgün bir işlevi uygulamak isteyen bizim zaman harcama.
string_agg
v.Next'e eklenecek. ve bunların hepsi gidebilir.
Bu yanıt beklenmedik sonuçlar döndürebilir Tutarlı sonuçlar için diğer yanıtlarda ayrıntılı olarak belirtilen FOR XML PATH yöntemlerinden birini kullanın.
Kullanım COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
Sadece bir açıklama (çünkü bu cevap nispeten düzenli görüşlere sahip gibi görünüyor):
1) @Names
Boş bir dize değeri ile başlatmaya gerek yoktur .
2) Sonunda fazladan bir ayırıcıyı çıkarmaya gerek yoktur.
@Names
NULL o satır sonra ve sonraki satır yine boş bir dize olarak baştan başlar. Kolayca iki biriyle sabit çözümleri:DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL
veya:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') +
ISNULL(Name, 'N/A')
FROM People
İstediğiniz davranışa bağlı olarak (ilk seçenek yalnızca NULL'ları filtreler , ikinci seçenek onları bir işaretçi iletisiyle [ sizin için uygun olanla değiştir '' yerine tutar).
SQL Server'ın bir sonraki sürümünden başlayarak, herhangi bir değişken veya XML cadılığına başvurmak zorunda kalmadan nihayet satırları birleştirebiliriz.
Gruplama olmadan
SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;
Gruplama ile:
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Gruplama ve alt sıralama ile
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Henüz bir yöntem, XML
data()
MS SQL Server'daki komut :
NameList adlı tabloyu FName adlı bir sütunla varsayalım,
SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')
İadeler:
"Peter, Paul, Mary, "
Sadece ekstra virgül ele alınmalıdır.
Düzenleme: @ NReilingh yorumundan kabul edildiği gibi, sondaki virgül kaldırmak için aşağıdaki yöntemi kullanabilirsiniz. Aynı tablo ve sütun adlarını varsayarak:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
+ ', '
her birleştirilmiş eleman arasında hala tek bir boşluk eklediğini fark ediyorum .
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'')
kullanabilirsiniz İÇİN JSON sözdizimi
yani
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
Ve sonuç olacak
Id Emails
1 abc@gmail.com
2 NULL
3 def@gmail.com, xyz@gmail.com
Bu, verileriniz geçersiz XML karakterleri içerdiğinde bile çalışır
'"},{"_":"'
Eğer veri içeriyorsa, çünkü güvenlidir'"},{"_":"',
bunun kaçan edilecek"},{\"_\":\"
', '
Herhangi bir dize ayırıcı ile değiştirebilirsiniz
<
, >
, &
vb hangi FOR XML PATH('')
otomatik kaçacak.
MySQL'de , birden çok satırdaki değerleri birleştirmenizi sağlayan GROUP_CONCAT () işlevi vardır . Misal:
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 '", "'
son girişin sonunda bazı karakterleri özleyeceğim. bu neden olabilir?
CHAR
, bunu döküm gerekir, örneğin via GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2) birçok değerleri gelecek varsa, artmalıdır group_concat_max_len
yazılı olarak stackoverflow.com/a/1278210/1498405
COALESCE kullanın - Buradan daha fazla bilgi edinin
Örnek olarak:
102
103
104
Sonra sql sunucusuna aşağıdaki kodu yazın,
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
Çıktı:
102,103,104
Declare @Numbers AS Nvarchar(MAX)
ve iyi çalıştı. Kullanmamanızı neden tavsiye ettiğinizi açıklayabilir misiniz?
Postgres dizileri harika. Misal:
Bazı test verileri oluşturun:
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)
Bunları bir dizide toplayın:
test=# select array_agg(name) from names;
array_agg
-------------------
{Peter,Paul,Mary}
(1 row)
Diziyi virgülle ayrılmış bir dizeye dönüştürün:
test=# select array_to_string(array_agg(name), ', ') from names;
array_to_string
-------------------
Peter, Paul, Mary
(1 row)
YAPILAN
PostgreSQL 9.0'dan bu yana daha da kolay .
select array_to_string(array_agg(name||'('||id||')'
Oracle 11g Release 2, LISTAGG işlevini destekler. Belgeler burada .
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.
Sonuçta ortaya çıkan dizenin 4000 karakterden fazla çıkma olasılığı varsa bu işlevi uygulamaya dikkat edin. Bir istisna atar. Bu durumda, istisnayı ele almanız veya birleştirilen dizenin 4000 karakterden fazla olmasını engelleyen kendi işlevinizi yuvarlamanız gerekir.
LISTAGG
mükemmel çalışıyor! Burada bağlanan dokümanı okumanız yeterli. wm_concat
sürüm 12c'den kaldırılmıştır.
SQL Server 2005 ve sonraki sürümlerde, satırları birleştirmek için aşağıdaki sorguyu kullanın.
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
<
veya gibi XML sembolleri içerdiğinde bu başarısız olduğuna inanıyorum &
.
Evde bir SQL Server'a erişimim yok, bu yüzden sözdiziminde sanırım, ama az ya da çok:
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, '')
Yinelemeli bir CTE çözümü önerildi, ancak kod sağlanmadı. Aşağıdaki kod, özyinelemeli bir CTE örneğidir. Sonuçlar soruyla eşleşse de, tablodaki tüm satırları değil, bunu gerçekten satır satırlarında yapmak istediğinizi varsaydığından , veriler verilen açıklamayla tamamen eşleşmiyor. Bunu tablodaki tüm satırlarla eşleşecek şekilde değiştirmek okuyucu için bir alıştırma olarak bırakılır.
;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
4 virgülle ayrılmış dizeye sütun grupları arasında id
s. İlk bakışta, bu SQL Server için diğer çözümlerin çoğundan daha fazla iş olduğunu düşünüyorum.
PostgreSQL 9.0'dan başlayarak bu oldukça basittir:
select string_agg(name, ',')
from names;
array_agg()
9.0'dan önceki sürümlerde hgmnz tarafından gösterildiği gibi kullanılabilir
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
veyachar
SQL Server vNext'te bu, STRING_AGG işlevi ile oluşturulacak, daha fazla bilgiyi buradan edinebilirsiniz: https://msdn.microsoft.com/en-us/library/mt790580.aspx
XML kullanmak satırları virgülle ayırmama yardımcı oldu. Ek virgül için SQL Server'ın replace işlevini kullanabiliriz. Virgül eklemek yerine, AS 'data ()' kullanımı satırları boşluklarla birleştirir ve daha sonra aşağıda yazılan sözdizimi olarak virgülle değiştirilebilir.
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
Ek virgül kullanmadan kullanıma hazır bir çözüm:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
Boş bir liste NULL değerine neden olacaktır. Genellikle listeyi bir tablo sütununa veya program değişkenine eklersiniz: 255 maksimum uzunluğu ihtiyacınıza göre ayarlayın.
(Diwakar ve Jens Frandsen iyi cevaplar verdiler, ancak iyileştirilmeleri gerekiyor.)
', '
ile ','
size fazladan bir boşluk istemiyorsanız.
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')
İşte bir örnek:
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)
Bu, başıboş virgülün başına koyar.
Ancak, başka sütunlara ihtiyacınız varsa veya bir alt tabloyu CSV yapmak için bunu skaler kullanıcı tanımlı bir alana (UDF) sarmanız gerekir.
XML yolunu SELECT yan tümcesinde de ilişkili bir alt sorgu olarak kullanabilirsiniz (ancak Google evde iş işleri yapmadığı için işe geri dönene kadar beklemek zorundayım :-)
Diğer cevaplarla yanıtı okuyan kişi, araç veya öğrenci gibi belirli bir alan tablosundan haberdar olmalıdır. Bir çözümü test etmek için tablo oluşturulmalı ve verilerle doldurulmalıdır.
Aşağıda SQL Server "Information_Schema.Columns" tablosunu kullanan bir örnek bulunmaktadır. Bu çözümü kullanarak hiçbir tablo oluşturulmasına veya veri eklenmesine gerek yoktur. Bu örnek, veritabanındaki tüm tablolar için virgülle ayrılmış sütun adları listesi oluşturur.
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
Oracle DB'ler için şu soruya bakın: Birden çok satır, saklı yordam oluşturmadan Oracle'daki bir satır içine nasıl birleştirilebilir?
En iyi yanıt, Oracle 11g Sürüm 2 ve sonraki sürümlerde bulunan yerleşik LISTAGG () işlevini kullanarak @Emmanuel tarafından görünüyor.
SELECT question_id,
LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
@ user762952'nin işaret ettiği gibi ve Oracle'ın belgelerine göre http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php WM_CONCAT () işlevi de bir seçenektir. Kararlı görünüyor, ancak Oracle açıkça herhangi bir uygulama SQL'i için kullanılmasını öneriyor, bu nedenle kendi sorumluluğunuzdadır.
Bunun dışında kendi işlevinizi yazmak zorunda kalacaksınız; yukarıdaki Oracle belgesinde bunun nasıl yapılacağı ile ilgili bir kılavuz vardır.
Boş değerlerden kaçınmak için CONCAT () kullanabilirsiniz
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
Dana'nın cevabının şıklığını gerçekten çok sevdim . Sadece tamamlamak istedim.
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
o zaman daha sonra kesmek zorunda değilsiniz.
Bu yanıtın çalışması için sunucuda bir miktar ayrıcalık gerekir.
Montajlar sizin için iyi bir seçenektir. Nasıl oluşturulacağını açıklayan birçok site var. Ben çok iyi açıklanmıştır düşünüyorum biri şudur biri
İsterseniz, montajı zaten oluşturdum ve DLL'i buradan indirmek mümkündür .
İndirdikten sonra, SQL Server'ınızda aşağıdaki komut dosyasını çalıştırmanız gerekir:
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
Montaj yolunun sunucu tarafından erişilebilir olabileceğini gözlemleyin. Tüm adımları başarıyla tamamladığınızdan, aşağıdaki gibi işlevi kullanabilirsiniz:
SELECT dbo.Concat(field1, ',')
FROM Table1
Umarım yardımcı olur!!!
MySQL tamamlandı Örnek:
Birçok Veriye sahip olabilen Kullanıcılarımız var ve bir çıktıya sahip olmak istiyoruz, burada tüm kullanıcıların Verilerini bir listede görebiliriz:
Sonuç:
___________________________
| id | rowList |
|-------------------------|
| 0 | 6, 9 |
| 1 | 1,2,3,4,5,7,8,1 |
|_________________________|
Tablo Ayarları:
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);
Sorgu:
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
Ben genellikle SQL Server dizeleri birleştirmek için böyle seçin kullanın:
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
Bu benim için çalıştı ( SqlServer 2016 ):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
İşte kaynak: https://www.mytecbits.com/
Ve MySql için bir çözüm (bu sayfa MySql için Google'da gösterildiğinden)
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
Gönderen MySQL Dokümantasyon
Oracle'da öyle wm_concat
. Bu fonksiyonun 10g ve üzeri sürümlerde mevcut olduğuna inanıyorum .
Bu da yararlı olabilir
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
İadeler
Peter,Paul,Mary