Birden çok satırdaki metni SQL sunucusundaki tek bir metin dizesine nasıl birleştirebilirim?


1913

Üç 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?


26
SQL Server'a özgü yanıtlar için bu soruyu deneyin .
Matt Hamilton

17
MySQL için, kontrol Group_Concat gelen bu cevap
Pykler

26
SQL Server'ın bir sonraki sürümü FOR XML PATH silliness olmadan zarif çok satır dize bitişik çözmek için yeni bir özellik sunmak isterdim.
Pete Alvin

4
SQL değil, ancak bu yalnızca bir kez bir şeyse, listeyi bu tarayıcı içi araca yapıştırabilirsiniz.town/ column
Stack Man

3
Oracle'da, aynı işlemi yapan WM_CONCAT (COLUMN_NAME) adlı desteklenmeyen bir işlev bulunmadan önce 11g r2'den itibaren LISTAGG'yi (COLUMN_NAME) kullanabilirsiniz.
Richard

Yanıtlar:


1432

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 PATHyö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 substringbitirebilir 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

13
Harika bir çözüm. HTML'deki gibi özel karakterleri işlemeniz gerekirse, aşağıdakiler yararlı olabilir: Rob Farley: FOR XML PATH ('') ile özel karakterleri işleme .

10
Görünüşe göre adlar <veya gibi XML karakterleri içeriyorsa bu işe yaramaz &. @ BenHinman'ın yorumuna bakın.
Sam

23
Not: Bu yöntem, belgelenmemiş davranışına dayanmaktadır 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.
Bacon Bits

26
@Whelkaholism Sonuç olarak FOR XML, rastgele dizeleri birleştirmek yerine XML üretmeyi amaçlamaktadır. İşte bu kaçar yüzden &, <ve >XML varlığı kodlarına ( &amp;, &lt;, &gt;). Ben de kaçmak varsayıyorum "ve 'hiç &quot;ve &apos;ö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.
Bacon Bits

13
İyi haber: MS SQL Server string_aggv.Next'e eklenecek. ve bunların hepsi gidebilir.
Jason C

1009

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

  • Coalesce gerçekten sadece iki şeyi başaran yararlı bir hile:

1) @NamesBoş bir dize değeri ile başlatmaya gerek yoktur .

2) Sonunda fazladan bir ayırıcıyı çıkarmaya gerek yoktur.

  • Bir satır bir varsa çözüm yukarıdaki yanlış sonuçlar verecektir BOŞ bir varsa (Ad değerini NULL , NULL yapacak @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).


72
Açıkça belirtmek gerekirse, birleşme listeyi oluşturmakla hiçbir ilgisi yoktur, sadece NULL değerlerinin dahil edilmediğinden emin olur.
Graeme Perrow

17
@Graeme Perrow NULL değerlerini hariç tutmaz (bunun için bir WHERE gereklidir - giriş değerlerinden biri NULL ise sonuç kaybedilir ) ve bu yaklaşımda gereklidir çünkü: NULL + NULL olmayan -> NULL ve NULL olmayan + NULL -> NULL; Ayrıca @Ad varsayılan olarak NULL'dur ve aslında bu özellik bir ',' eklenip eklenmeyeceğini belirlemek için burada örtük bir sentinel olarak kullanılır.

62
Bu birleştirme yönteminin, sorguyu belirli bir planla yürüttüğü SQL Server'a bağlı olduğunu lütfen unutmayın. Ben (bir SİPARİŞ BY eklenerek) bu yöntemi kullanarak yakalandı. Ne zaman az sayıda satır ile uğraşırken iyi çalıştı ama daha fazla veri ile SQL Server, herhangi bir birleşim olmadan ilk öğeyi seçerek sonuçlanan farklı bir plan seçti. Bu makaleye bak Anith Sen.
fbarber

17
TSQL değişkeni kullandığından, bu yöntem bir seçim listesinde veya where-deyiminde alt sorgu olarak kullanılamaz. Bu gibi durumlarda @Ritesh
R. Schreurs

11
Bu güvenilir bir birleştirme yöntemi değildir. Desteklenmez ve kullanılmamalıdır (Microsoft'a göre, örneğin, support.microsoft.com/en-us/kb/287515 , connect.microsoft.com/SQLServer/Feedback/Details/704389 ). Uyarı yapmadan değiştirilebilir. Stackoverflow.com/questions/5031204/… 'de tartışılan XML PATH tekniğini kullanın Burada daha fazla yazdım: marc.durdin.net/2015/07/…
Marc

454

SQL Server 2017+ ve SQL Azure: STRING_AGG

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.

STRING_AGG (Transact-SQL)

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;

2
Ve CLR çözümlerinin aksine, sıralama üzerinde kontrolünüz vardır.
canon

STRING_AGG'de 4000 karakterlik ekran sınırlaması var gibi görünüyor
InspiredBy tarafından

GROUP BY olmadığında sıralama yapmanın bir yolu var mı (yani "Gruplama olmadan" örneği için)?
RuudvK

Güncelleme: Aşağıdakileri yapmayı başardım, ancak daha temiz bir yol var mı? SEÇİN STRING_AGG (Ad, ',') AS Departmanlardan (SELECT TOP 100000 Ad İnsan Kaynağından SEÇİN. Bölüm ORDER BY Ad) D;
RuudvK

362

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

15
kutsal s ** t inanılmaz! Kendi başınıza yürütüldüğünde, örneğin örneğinizde olduğu gibi sonuç, tıklandığında (SSMS'de) verileri içeren yeni bir pencere açar, ancak daha büyük bir sorgunun parçası olarak kullanıldığında yalnızca bir dize olarak görünür. Bu bir dize mi? ya da bu verileri kullanacak uygulamada farklı davranmanız gereken xml mi?
Ben

10
Bu yaklaşım XML ve <ve> gibi karakterlerden de kaçar. Böylece, '<b>' + FName + '</b>' SEÇİLMESİ, "<b> John" / b> Paul ... "ile
sonuçlanır

8
Temiz çözüm. Eklemediğimde bile, + ', 'her birleştirilmiş eleman arasında hala tek bir boşluk eklediğini fark ediyorum .
Baodad

8
@Baodad Bu anlaşmanın bir parçası gibi görünüyor. Eklenen bir belirteç karakterini değiştirerek geçici çözüm bulabilirsiniz. Örneğin, bu herhangi bir uzunluk için virgülle ayrılmış mükemmel bir liste yapar:SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
NReilingh

1
Vay be, aslında benim test kullanarak veri () ve yerine WAY daha performanslı. Çok garip.
NReilingh

306

Gelen 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'da

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


Ve SQL Server 2017'de Azure SQL Veritabanı

Yeni STRING_AGG işlevini kullanabilirsiniz


3
Önde gelen iki karakteri birleştirmek için STUFF işlevini iyi kullanın.
David

3
Bu çözümü en çok sevdim, çünkü '<label>' olarak ekleyerek seçim listesinde kolayca kullanabilirim. @Ritesh çözümü ile bunu nasıl yapacağımdan emin değilim.
R.Schreurs

13
Bu seçenek ayrıca gibi un-kaçan XML Saklıdır karakterleri işlemesi nedeniyle kabul cevap daha iyidir <, >, &vb hangi FOR XML PATH('')otomatik kaçacak.
BateTech

Bu, sorunu çözdüğü ve şimdi SQL'in farklı sürümlerinde bir şeyler yapmanın en iyi yollarını sağladığı için harika bir yanıttır. 2017 / Azure
Chris Ward

120

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

iyi çalışıyor. Ama kullandığım zaman SEPARATOR '", "'son girişin sonunda bazı karakterleri özleyeceğim. bu neden olabilir?
gooleem

@gooleem Ne demek istediğiniz konusunda net değilim, ancak bu işlev yalnızca ayırıcıyı öğeler arasına koyar, sonra değil. Cevap bu değilse, yeni bir soru göndermenizi tavsiye ederim.
Darryl Hein

@DarrylHein benim ihtiyaçları için yukarıdaki gibi ayırıcı kullanılır. Ama bu, çıktının sonunda bazı karakterleri kesiyor. Bu çok garip ve bir hata gibi görünüyor. Bir çözümüm yok, sadece iş başındayım.
gooleem

Temelde çalışır. İki şey dikkate almak: 1) sütun bir değilse 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_lenyazılı olarak stackoverflow.com/a/1278210/1498405
hardmooth

58

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

2
Bu gerçekten FOR XML sunduğu kodlama sorunları önler IMO en iyi çözümdür. Kullandım Declare @Numbers AS Nvarchar(MAX)ve iyi çalıştı. Kullanmamanızı neden tavsiye ettiğinizi açıklayabilir misiniz?
EvilDr

7
Bu çözüm 8 yıl önce gönderildi! stackoverflow.com/a/194887/986862
Andre Figueiredo

Bu sorgu neden geri dönüyor ??? Kiril yerine sembol? Bu sadece çıktı sorunu mu?
Akmal Salikhov

48

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 .


Birden fazla sütuna ihtiyacınız varsa, örneğin parantez içindeki çalışan kimliği concat operatörünü kullanır: select array_to_string(array_agg(name||'('||id||')'
Richard Fox

Sql-server için geçerli değildir , sadece mysql için geçerlidir
GoldBishop

46

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.

Uyarı

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.


1
Oracle'ın eski sürümleri için wm_concat mükemmeldir. Kullanımı Alex tarafından verilen bağlantı hediyesinde açıklanmaktadır. Teşekkürler Alex!
toscanelli

LISTAGGmükemmel çalışıyor! Burada bağlanan dokümanı okumanız yeterli. wm_concatsürüm 12c'den kaldırılmıştır.
16:56

34

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

2
Değerler <veya gibi XML sembolleri içerdiğinde bu başarısız olduğuna inanıyorum &.
Sam

28

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

11
Boş olmayan bir şeye @ isimleri başlatmanız gerekir, aksi takdirde NULL alırsınız; sınırlayıcıyı da kullanmanız gerekir (gereksiz olanlar dahil)
Marc Gravell

3
Bu yaklaşımla ilgili tek sorun (ki her zaman kullanıyorum)
gömemezsiniz

1
Önde gelen alandan kurtulmak için sorguyu değiştirinSELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
Tian van Heerden

Ayrıca, Adın boş olmadığını kontrol etmelisiniz, bunu yaparak yapabilirsiniz:SELECT @names = @names + ISNULL(' ' + Name, '')
Vita1ij

28

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

1
Mama için: Geçici basetable içine bu sorgu ekler 12 satır (3 sütun), daha sonra bir özyinelemeli ortak tablo Expression (rCTE) oluşturur ve daha sonra düzleştirir name 4 virgülle ayrılmış dizeye sütun grupları arasında ids. İlk bakışta, bu SQL Server için diğer çözümlerin çoğundan daha fazla iş olduğunu düşünüyorum.
17'de

2
@knb: bunun övgü, kınama veya sadece sürpriz olup olmadığından emin değilim. Temel tablo, örneklerimin gerçekten çalışmasını sevdiğimden, bunun soru ile ilgisi olmadığı için.
jmoreno

26

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


Bunu metin türü olmayan sütunlarla yapmak için, bir tür döküm eklemeniz gerekir:SELECT string_agg(non_text_type::text, ',') FROM table
Torben Kohlmeier

@TorbenKohlmeier: sadece karakter olmayan sütunlar için buna ihtiyacınız vardır (örn. Tamsayı, ondalık). Bu sadece para cezası işleri varcharveyachar
a_horse_with_no_name

26

Nihai sonucunuzu tutacak bir değişken oluşturmanız ve bu şekilde seçmeniz gerekir.

En Kolay Çözüm

DECLARE @char VARCHAR(MAX);

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

PRINT @char;


18

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(''))
         , ' ', ', ') 

2
Bu benim düşüncemdeki en iyi cevap. Başka bir masaya katılmanız gerektiğinde declare değişkeninin kullanımı iyi değildir ve bu güzel ve kısadır. İyi iş.
David Roussel

7
FName verilerinin zaten boşlukları varsa bu iyi çalışmaz, örneğin "My Name"
binball

Gerçekten benim için ms-sql 2016 üzerinde çalışıyor xml yolu ('') için Marka Burada Kimlik IN (1,2,3,4) 'dan REPLACE ((Name AS' data () 'öğesini seçin),' ',' , ') gibi tüm Markalar
Rejwanul Reja

17

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


Bunu kullanırken virgülden önce bir boşluk var :(
slayernoah

1
Sadece yerini ', 'ile ','size fazladan bir boşluk istemiyorsanız.
Daniel Reis

13
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

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


10

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 

7

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.


7

Boş değerlerden kaçınmak için CONCAT () kullanabilirsiniz

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

CONCAT'ın neden işe yaradığını bilmek güzel olurdu . MSDN bağlantısı iyi olurdu.
Ters Mühendis

7

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)

Son iki sembolü ',' siliyorsanız, Ad'dan sonra ',' eklemeniz gerekir ('SELECT \ @names = \ @names + Name +', 'FROM Names'). Bu şekilde son iki karakter daima ',' olacaktır.
JT_

Benim durumumda önde gelen virgül kurtulmak gerekiyordu bu yüzden sorguyu değiştirmek SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Nameso zaman daha sonra kesmek zorunda değilsiniz.
Tian van Heerden

6

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


1
DLL bağlantısı bir 404 hatasıdır. Bunun için bir montaj kullanmak aşırıdır. SQL Server için en iyi cevaba bakın .
Şubat'ta

6

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

5

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

5

Eğer null'larla uğraşmak istiyorsanız, bir nereye yan tümce ekleyerek veya ilki çevresine başka bir COALESCE ekleyerek bunu yapabilirsiniz.

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

5

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



4

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

5
Ne yazık ki bu davranış resmi olarak desteklenmiyor gibi görünüyor. MSDN diyor ki: "Bir değişkene seçme listesinde başvurulursa, değişkene bir skaler değer atanmalı veya SELECT deyimi yalnızca bir satır döndürmelidir." Ve sorunları gözlemleyen insanlar var: sqlmag.com/sql-server/multi-row-variable-assignment-and-order
blueling
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.