Come ottengo:
id Name Value
1 A 4
1 B 8
2 C 9
per
id Column
1 A:4, B:8
2 C:9
Come ottengo:
id Name Value
1 A 4
1 B 8
2 C 9
per
id Column
1 A:4, B:8
2 C:9
Risposte:
Nessun CURSORE, ciclo WHILE o funzione definita dall'utente necessari .
Devo solo essere creativo con FOR XML e PATH.
[Nota: questa soluzione funziona solo su SQL 2005 e versioni successive. La domanda originale non specificava la versione in uso.]
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
Se si tratta di SQL Server 2017 o SQL Server Vnext, SQL Azure è possibile utilizzare string_agg come di seguito:
select id, string_agg(concat(name, ':', [value]), ', ')
from #YourTable
group by id
l'utilizzo del percorso XML non si concatenerà perfettamente come ci si potrebbe aspettare ... sostituirà "&" con "& amp;" e rovineremo anche <" and ">
... forse qualche altra cosa, non sono sicuro ... ma puoi provare questo
Mi sono imbattuto in una soluzione alternativa per questo ... è necessario sostituire:
FOR XML PATH('')
)
con:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
... o NVARCHAR(MAX)
se è quello che stai usando.
perché diavolo non SQL
ha una funzione aggregata concatenata? questa è una PITA.
Mi sono imbattuto in un paio di problemi, quando ho provato a convertire il suggerimento di Kevin Fairchild per il lavoro con le stringhe contenenti spazi e caratteri speciali XML ( &
, <
, >
) che sono stati codificati.
La versione finale del mio codice (che non risponde alla domanda originale ma può essere utile a qualcuno) è simile alla seguente:
CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
Anziché utilizzare uno spazio come delimitatore e sostituire tutti gli spazi con virgole, anticipa semplicemente una virgola e uno spazio per ciascun valore, quindi utilizza STUFF
per rimuovere i primi due caratteri.
La codifica XML viene gestita automaticamente utilizzando la direttiva TYPE .
Un'altra opzione che utilizza SQL Server 2005 e versioni successive
---- test data
declare @t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert @t select 1125439 ,'CKT','Approved'
insert @t select 1125439 ,'RENO','Approved'
insert @t select 1134691 ,'CKT','Approved'
insert @t select 1134691 ,'RENO','Approved'
insert @t select 1134691 ,'pn','Approved'
---- actual query
;with cte(outputid,combined,rn)
as
(
select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
from @t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
Installare gli aggregati SQLCLR da http://groupconcat.codeplex.com
Quindi puoi scrivere codice in questo modo per ottenere il risultato che hai richiesto:
CREATE TABLE foo
(
id INT,
name CHAR(1),
Value CHAR(1)
);
INSERT INTO dbo.foo
(id, name, Value)
VALUES (1, 'A', '4'),
(1, 'B', '8'),
(2, 'C', '9');
SELECT id,
dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM dbo.foo
GROUP BY id;
SQL Server 2005 e versioni successive ti consentono di creare le tue funzioni di aggregazione personalizzate , anche per cose come la concatenazione. Vedi l'esempio in fondo all'articolo collegato.
Otto anni dopo ... Microsoft SQL Server vNext Database Engine ha finalmente migliorato Transact-SQL per supportare direttamente la concatenazione di stringhe raggruppate. L'anteprima tecnica della comunità versione 1.0 ha aggiunto la funzione STRING_AGG e CTP 1.1 ha aggiunto la clausola WITHIN GROUP per la funzione STRING_AGG.
Riferimento: https://msdn.microsoft.com/en-us/library/mt775028.aspx
Questa è solo un'aggiunta al post di Kevin Fairchild (molto intelligente tra l'altro). Lo avrei aggiunto come commento, ma non ho ancora abbastanza punti :)
Stavo usando questa idea per una vista su cui stavo lavorando, tuttavia gli elementi che stavo concatenando contenevano spazi. Quindi ho modificato leggermente il codice per non usare spazi come delimitatori.
Ancora grazie per la bella soluzione Kevin!
CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT )
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9)
SELECT [ID],
REPLACE(REPLACE(REPLACE(
(SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A
FROM #YourTable
WHERE ( ID = Results.ID )
FOR XML PATH (''))
, '</A><A>', ', ')
,'<A>','')
,'</A>','') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
In Oracle è possibile utilizzare la funzione aggregata LISTAGG.
Record originali
name type
------------
name1 type1
name2 type2
name2 type3
Sql
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
Risultato in
name type
------------
name1 type1
name2 type2; type3
Questo tipo di domanda viene posta qui molto spesso e la soluzione dipenderà molto dai requisiti sottostanti:
https://stackoverflow.com/search?q=sql+pivot
e
https://stackoverflow.com/search?q=sql+concatenate
In genere, non esiste un solo modo SQL per farlo senza sql dinamico, una funzione definita dall'utente o un cursore.
Solo per aggiungere a ciò che ha detto Cade, questa è di solito una cosa di visualizzazione front-end e dovrebbe quindi essere gestita lì. So che a volte è più facile scrivere qualcosa al 100% in SQL per cose come l'esportazione di file o altre soluzioni "solo SQL", ma il più delle volte questa concatenazione dovrebbe essere gestita nel tuo livello di visualizzazione.
Non è necessario un cursore ... un ciclo while è sufficiente.
------------------------------
-- Setup
------------------------------
DECLARE @Source TABLE
(
id int,
Name varchar(30),
Value int
)
DECLARE @Target TABLE
(
id int,
Result varchar(max)
)
INSERT INTO @Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO @Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO @Source(id, Name, Value) SELECT 2, 'C', 9
------------------------------
-- Technique
------------------------------
INSERT INTO @Target (id)
SELECT id
FROM @Source
GROUP BY id
DECLARE @id int, @Result varchar(max)
SET @id = (SELECT MIN(id) FROM @Target)
WHILE @id is not null
BEGIN
SET @Result = null
SELECT @Result =
CASE
WHEN @Result is null
THEN ''
ELSE @Result + ', '
END + s.Name + ':' + convert(varchar(30),s.Value)
FROM @Source s
WHERE id = @id
UPDATE @Target
SET Result = @Result
WHERE id = @id
SET @id = (SELECT MIN(id) FROM @Target WHERE @id < id)
END
SELECT *
FROM @Target
Diventiamo molto semplici:
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
Sostituisci questa riga:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
Con la tua domanda.
non ho visto risposte incrociate, inoltre non è necessario estrarre xml. Ecco una versione leggermente diversa di ciò che ha scritto Kevin Fairchild. È più veloce e più facile da usare in query più complesse:
select T.ID
,MAX(X.cl) NameValues
from #YourTable T
CROSS APPLY
(select STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = T.ID)
FOR XML PATH(''))
,1,2,'') [cl]) X
GROUP BY T.ID
È possibile migliorare le prestazioni in modo significativo nel modo seguente se raggruppa per contiene principalmente un elemento:
SELECT
[ID],
CASE WHEN MAX( [Name]) = MIN( [Name]) THEN
MAX( [Name]) NameValues
ELSE
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
END
FROM #YourTable Results
GROUP BY ID
Utilizzo della funzione Sostituisci e FOR JSON PATH
SELECT T3.DEPT, REPLACE(REPLACE(T3.ENAME,'{"ENAME":"',''),'"}','') AS ENAME_LIST
FROM (
SELECT DEPT, (SELECT ENAME AS [ENAME]
FROM EMPLOYEE T2
WHERE T2.DEPT=T1.DEPT
FOR JSON PATH,WITHOUT_ARRAY_WRAPPER) ENAME
FROM EMPLOYEE T1
GROUP BY DEPT) T3
Per dati di esempio e altri modi clicca qui
Se hai abilitato clr puoi usare la libreria Group_Concat di GitHub
GROUP_CONCAT()
funzione aggregata, ma risolverlo su Microsoft SQL Server è più imbarazzante. Vedere la seguente domanda SO di aiuto: " Come ottenere più record contro un record basato sulla relazione? "