Domanda

Considera una tabella di database con nomi, con tre righe:

Peter
Paul
Mary

C'è un modo semplice per trasformarlo in una singola stringa di Peter, Paul, Mary ?

È stato utile?

Soluzione

Se si utilizza SQL Server 2017 o Azure, vedere Risposta Mathieu Renda .

Ho avuto un problema simile quando stavo cercando di unire due tavoli con relazioni uno-a-molti. In SQL 2005 ho scoperto che il metodo XML PATH è in grado di gestire molto facilmente la concatenazione delle righe.

Se esiste una tabella chiamata STUDENTS

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Il risultato che mi aspettavo era:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

Ho usato il seguente 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]

Puoi fare la stessa cosa in modo più compatto se riesci a concatenare le virgole all'inizio e utilizzare sottostringa per saltare il primo in modo da non dover eseguire una sottoquery :

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

Altri suggerimenti

  

Questa risposta può restituire risultati imprevisti Per risultati coerenti, utilizzare uno dei metodi PER XML PERCORSO dettagliati in altre risposte.

Usa COALESCE :

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

Solo qualche spiegazione (poiché questa risposta sembra avere viste relativamente regolari):

  • Coalesce è davvero solo un trucco utile che realizza due cose:

1) Non è necessario inizializzare @Names con un valore stringa vuoto.

2) Non è necessario rimuovere un separatore aggiuntivo alla fine.

  • La soluzione sopra mostrerà risultati errati se una riga ha un valore NULL Nome (se c'è un NULL , il NULL farà @Names NULL dopo quella riga e la riga successiva ricomincerà da capo come stringa vuota. Risolto facilmente con una delle due soluzioni:
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

o

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

A seconda del comportamento desiderato (la prima opzione filtra solo NULL , la seconda opzione li mantiene nell'elenco con un messaggio marcatore [sostituisci 'N / A' con tutto ciò che è appropriato per si]).

Un metodo non ancora mostrato tramite il comando XML data () in MS SQL Server è:

Supponi una tabella chiamata NameList con una colonna chiamata FName,

SELECT FName + ', ' AS 'data()' 
FROM NameList 
FOR XML PATH('')

ritorna:

"Peter, Paul, Mary, "

Deve essere gestita solo la virgola aggiuntiva.

Modifica: Come adottato dal commento di @ NReilingh, puoi utilizzare il seguente metodo per rimuovere la virgola finale. Supponendo gli stessi nomi di tabella e colonna:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

SQL Server 2017+ e SQL Azure: STRING_AGG

A partire dalla prossima versione di SQL Server, possiamo finalmente concatenare le righe senza dover ricorrere a variabili o stregonerie XML.

STRING_AGG (Transact-SQL)

Senza raggruppamento

SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;

Con raggruppamento:

SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

Con raggruppamento e ordinamento secondario

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department 
GROUP BY GroupName;

In SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

In SQL Server 2016

puoi usare PER LA sintassi JSON

vale a dire.

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

E il risultato diventerà

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

Funzionerà anche se i tuoi dati contengono caratteri XML non validi

il '"}, {" _ ": "' è sicuro perché se i tuoi dati contengono '"}, {" _ ": "', verrà convertito in "},{\"_\":\"

Puoi sostituire ',' con qualsiasi separatore di stringhe


E in SQL Server 2017, Database SQL di Azure

Puoi utilizzare il nuovo STRING_AGG funzione

In MySQL c'è una funzione, GROUP_CONCAT () , che consente di concatenare i valori da più righe. Esempio:

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

Usa COALESCE - Ulteriori informazioni da qui

Per un esempio:

  

102

     

103

     

104

Quindi scrivi sotto il codice nel server 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

L'output sarebbe:

102,103,104

Gli array Postgres sono fantastici. Esempio:

Crea alcuni dati di test:

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)

Aggregali in un array:

test=# select array_agg(name) from names;
 array_agg     
------------------- 
 {Peter,Paul,Mary}
(1 row)

Converti l'array in una stringa delimitata da virgole:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

Fine

Da PostgreSQL 9.0 è ancora più semplice .

Oracle 11g Release 2 supporta la funzione LISTAGG. Documentazione qui .

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.

Attenzione

Prestare attenzione a implementare questa funzione se esiste la possibilità che la stringa risultante superi i 4000 caratteri. Genererà un'eccezione. In tal caso, è necessario gestire l'eccezione o eseguire il rollup della propria funzione che impedisce alla stringa unita di superare i 4000 caratteri.

In SQL Server 2005 e versioni successive, utilizzare la query seguente per concatenare le righe.

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

Non ho accesso a un SQL Server a casa, quindi suppongo che la sintassi qui, ma sia più o meno:

DECLARE @names VARCHAR(500)

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

È stata suggerita una soluzione CTE ricorsiva, ma non è stato fornito alcun codice. Il codice seguente è un esempio di CTE ricorsivo - nota che sebbene i risultati corrispondano alla domanda, i dati non abbastanza corrispondono alla descrizione data, dato che presumo che tu voglia davvero farlo su gruppi di righe, non tutte le righe nella tabella. La modifica in modo che corrisponda a tutte le righe della tabella viene lasciata come esercizio per il lettore.

;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

Devi creare una variabile che conterrà il tuo risultato finale e selezionarla, in questo modo.

Soluzione più semplice

DECLARE @char VARCHAR(MAX);

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

PRINT @char;

A partire da PostgreSQL 9.0 questo è abbastanza semplice:

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

Nelle versioni precedenti alla 9.0 è possibile utilizzare array_agg () come mostrato da hgmnz

In SQL Server vNext questo sarà integrato con la funzione STRING_AGG, leggi di più qui: https://msdn.microsoft.com/en-us/library/mt790580.aspx

L'uso di XML mi ha aiutato a separare le righe con le virgole. Per la virgola aggiuntiva possiamo utilizzare la funzione di sostituzione di SQL & nbsp; Server. Invece di aggiungere una virgola, l'uso dell'AS 'data ()' concatenerà le righe con spazi, che in seguito possono essere sostituiti da virgole come sintassi scritta di seguito.

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

Una soluzione pronta per l'uso, senza virgole aggiuntive:

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

Un elenco vuoto comporterà un valore NULL. Di solito inserirai l'elenco in una colonna di tabella o variabile di programma: regola la lunghezza massima di 255 in base alle tue esigenze.

(Diwakar e Jens Frandsen hanno fornito buone risposte, ma necessitano di miglioramenti.)

SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

Ecco un esempio:

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)

Questo mette la virgola vagante all'inizio.

Tuttavia, se hai bisogno di altre colonne o di CSV di una tabella figlio, devi racchiuderla in un campo scalare definito dall'utente (UDF).

Puoi utilizzare il percorso XML come sottoquery correlata anche nella clausola SELECT (ma dovrei aspettare fino a quando non torno a lavorare perché Google non fa cose di lavoro a casa :-)

Con le altre risposte, la persona che legge la risposta deve essere a conoscenza di una tabella di dominio specifica come veicolo o studente. La tabella deve essere creata e popolata con i dati per testare una soluzione.

Di seguito è riportato un esempio che utilizza SQL Server " Information_Schema.Columns " tavolo. Utilizzando questa soluzione, non è necessario creare tabelle o aggiungere dati. In questo esempio viene creato un elenco separato da virgole di nomi di colonna per tutte le tabelle nel 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 

Per i DB Oracle, vedi questa domanda: Come possono essere concatenate più righe in una in Oracle senza creare una procedura memorizzata?

La risposta migliore sembra essere di @Emmanuel, usando la funzione LISTAGG () integrata, disponibile in Oracle 11g versione 2 e successive.

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

come sottolineato da @utente762952, e secondo la documentazione di Oracle http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , anche la funzione WM_CONCAT () è un'opzione. Sembra stabile, ma Oracle sconsiglia esplicitamente di usarlo per qualsiasi applicazione SQL, quindi usalo a tuo rischio.

Oltre a questo, dovrai scrivere la tua funzione; il documento Oracle sopra ha una guida su come farlo.

Mi è piaciuta molto l'eleganza della la risposta di Dana . Volevo solo completarlo.

DECLARE @names VARCHAR(MAX)
SET @names = ''

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

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)

Per evitare valori nulli puoi usare CONCAT ()

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

Questa risposta richiederà alcuni privilegi nel server per funzionare.

Assemblee sono una buona opzione per te. Ci sono molti siti che spiegano come crearlo. Quello che penso sia molto ben spiegato è questo one

Se lo desideri, ho già creato l'assembly ed è possibile scaricare la DLL qui .

Dopo averlo scaricato, sarà necessario eseguire il seguente script in SQL Server:

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

Osservare che il percorso dell'assemblaggio potrebbe essere accessibile al server. Poiché tutti i passaggi sono stati eseguiti correttamente, è possibile utilizzare la funzione come:

SELECT dbo.Concat(field1, ',')
FROM Table1

Spero che sia d'aiuto !!!

Di solito uso select in questo modo per concatenare stringhe in 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

Se vuoi gestire i valori null puoi farlo aggiungendo una clausola where o aggiungendo un'altra COALESCE attorno alla prima.

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

Esempio completo di MySQL:

Abbiamo utenti che possono avere molti dati e vogliamo avere un output, in cui possiamo vedere tutti i dati degli utenti in un elenco:

Risultato:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

Impostazione tabella:

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

Query:

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

In Oracle, è wm_concat . Credo che questa funzione sia disponibile nella versione 10g e successive.

Anche questo può essere utile

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

ritorna

Peter,Paul,Mary

Questo metodo si applica al database Teradata Aster solo poiché utilizza la sua funzione NPATH.

Ancora una volta, abbiamo gli studenti da tavolo

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Quindi con NPATH è solo SELEZIONA:

SELECT * FROM npath(
  ON Students
  PARTITION BY SubjectID
  ORDER BY StudentName
  MODE(nonoverlapping)
  PATTERN('A*')
  SYMBOLS(
    'true' as A
  )
  RESULT(
    FIRST(SubjectID of A) as SubjectID,
    ACCUMULATE(StudentName of A) as StudentName
  )
);

Risultato:

SubjectID       StudentName
----------      -------------
1               [John, Mary, Sam]
2               [Alaina, Edward]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top