Considerare 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
?
Considerare 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
?
Risposte:
Se si utilizza SQL Server 2017 o Azure, vedere la risposta di 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 XML PATH
metodo può 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 un modo più compatto se riesci a concatenare le virgole all'inizio e utilizzare substring
per saltare il primo in modo da non dover eseguire una query secondaria:
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
<
o &
. Vedi il commento di @ BenHinman.
FOR XML PATH ('')
. Ciò significa che non dovrebbe essere considerato affidabile in quanto qualsiasi patch o aggiornamento potrebbe alterare il modo in cui funziona. Fondamentalmente si basa su una funzione obsoleta.
FOR XML
scopo di generare XML, non concatenare stringhe arbitrarie. Ecco perché sfugge &
, <
e >
ai codici di entità XML ( &
, <
, >
). Suppongo che anche sfuggirà "
e '
per "
e '
negli attributi pure. Si Non GROUP_CONCAT()
, string_agg()
, array_agg()
, listagg()
, ecc, anche se è possibile tipo di fargli fare quello. Noi dovremmo spenderemo il nostro tempo chiedendo Microsoft implementare un corretto funzionamento.
string_agg
in v.Prossimo. e tutto questo può andare via.
Questa risposta può restituire risultati imprevisti Per risultati coerenti, utilizzare uno dei metodi FOR XML PATH dettagliati in altre risposte.
Utilizzare COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
Solo qualche spiegazione (poiché questa risposta sembra avere viste relativamente regolari):
1) Non è necessario inizializzare @Names
con un valore stringa vuoto.
2) Non è necessario rimuovere un separatore aggiuntivo alla fine.
@Names
NULL dopo quella riga e la riga successiva ricomincerà da capo come stringa vuota. Facilmente risolto 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 te]).
A partire dalla prossima versione di SQL Server, possiamo finalmente concatenare le righe senza ricorrere a variabili o stregonerie XML.
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 sotto-ordinamento
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Un metodo non ancora mostrato tramite il XML
data()
comando in MS SQL Server è:
Supponiamo che la tabella si chiami 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, è possibile 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
+ ', '
aggiunge comunque un singolo spazio tra ogni elemento concatenato.
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'')
puoi usare la sintassi FOR 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
Ciò funzionerà anche se i tuoi dati contengono caratteri XML non validi
il '"},{"_":"'
è sicuro perché se i dati contengono'"},{"_":"',
sarà sfuggito a"},{\"_\":\"
È possibile sostituire ', '
con qualsiasi separatore di stringhe
Puoi utilizzare la nuova funzione STRING_AGG
<
, >
, &
, ecc, che FOR XML PATH('')
sfuggirà automaticamente.
In MySQL è presente 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
SEPARATOR '", "'
mi mancheranno alcuni caratteri alla fine dell'ultima voce. perché può succedere?
CHAR
, è necessario lanciarlo, ad esempio tramite GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2) se si dispone di molti valori venire, è necessario aumentare il group_concat_max_len
come scritto in stackoverflow.com/a/1278210/1498405
Usa COALESCE - Scopri di più da qui
Per un esempio:
102
103
104
Quindi scrivere 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
Declare @Numbers AS Nvarchar(MAX)
e ha funzionato bene. Puoi spiegare perché mi consiglia di non usarlo per favore?
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)
FATTO
Da PostgreSQL 9.0 è ancora più semplice .
select array_to_string(array_agg(name||'('||id||')'
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.
Fare attenzione a implementare questa funzione se è possibile 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.
LISTAGG
funziona perfettamente! Basta leggere il documento collegato qui. wm_concat
rimosso dalla versione 12c in poi.
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
<
o &
.
Non ho accesso a un SQL Server a casa, quindi immagino che la sintassi qui, ma è più o meno:
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, '')
È 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 corrispondono esattamente alla descrizione fornita, dato che presumo che tu voglia davvero farlo su gruppi di righe, non su tutte le righe della 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
name
colonna in una stringa separata da virgole per 4 gruppi di id
s. A prima vista, penso che questo sia più lavoro di quello che fanno la maggior parte delle altre soluzioni per SQL Server.
A partire da PostgreSQL 9.0 questo è abbastanza semplice:
select string_agg(name, ',')
from names;
Nelle versioni precedenti alla 9.0 array_agg()
può essere usato come mostrato da hgmnz
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
ochar
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 usare la funzione di sostituzione di SQL 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 necessità.
(Diwakar e Jens Frandsen hanno fornito buone risposte, ma hanno bisogno di miglioramenti.)
', '
con ','
se non vuoi lo spazio extra.
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 la tabella "Information_Schema.Columns" di SQL Server. 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 si possono concatenare 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.
A parte questo, dovrai scrivere la tua funzione; il documento Oracle sopra ha una guida su come farlo.
Per evitare valori null è possibile utilizzare CONCAT ()
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
Mi è piaciuta molto l'eleganza della 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)
SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names
allora non devi troncare in seguito.
Questa risposta richiederà alcuni privilegi nel server per funzionare.
Le assemblee sono una buona opzione per te. Ci sono molti siti che spiegano come crearlo. Quello per me è molto ben spiegato è questo uno
Se lo desideri, ho già creato l'assembly ed è possibile scaricare qui la DLL .
Dopo averlo scaricato, dovrai eseguire il seguente script nel tuo 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. Dato che hai completato con successo tutti i passaggi, puoi utilizzare la funzione come:
SELECT dbo.Concat(field1, ',')
FROM Table1
Spero che sia d'aiuto!!!
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
Di solito uso select in questo modo per concatenare le 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
Questo ha funzionato per me ( SqlServer 2016 ):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
Ecco la fonte: https://www.mytecbits.com/
E una soluzione per MySql (poiché questa pagina viene visualizzata in Google per MySql)
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
Dalla documentazione di MySql
In Oracle, lo è 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