Domanda

Come posso ottenere:

id       Name       Value
1          A          4
1          B          8
2          C          9

a

id          Column
1          A:4, B:8
2          C:9
È stato utile?

Soluzione

Nessun CURSORE, ciclo WHILE o funzione definita dall'utente necessaria .

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

Altri suggerimenti

Se si tratta di SQL Server 2017 o SQL Server Vnext, SQL Azure è possibile utilizzare string_agg come indicato di seguito:

select id, string_agg(concat(name, ':', [value]), ', ')
    from #YourTable 
    group by id

l'uso del percorso XML non si concatenerà perfettamente come ci si potrebbe aspettare ... sostituirà " & amp; " con " & amp; amp; " e anche pasticciare con <" and "> ... forse alcune altre cose, 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 utilizzando.

perché diavolo SQL non ha una funzione aggregata concatenata? questa è una PITA.

Ho riscontrato un paio di problemi quando ho provato a convertire il suggerimento di Kevin Fairchild per lavorare con stringhe contenenti spazi e caratteri XML speciali (&, <, >) che sono stati codificati.

La versione finale del mio codice (che non risponde alla domanda originale ma può essere utile a qualcuno) è simile a questa:

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. &gt; &lt; 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 solo una virgola e uno spazio per ciascun valore, quindi utilizza STUFF per rimuovere i primi due caratteri.

La codifica XML viene gestita automaticamente utilizzando il TYPE direttiva.

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

Installa gli aggregati SQLCLR da http://groupconcat.codeplex.com

Quindi puoi scrivere codice come questo 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 , incluso per cose come la concatenazione, vedere 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 community 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/ biblioteca / mt775028.aspx

Un esempio potrebbe essere

In Oracle è possibile utilizzare la funzione di aggregazione 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

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 altri & Quot; SQL solo & Quot; soluzioni, ma la maggior parte delle volte questa concatenazione dovrebbe essere gestita nel tuo livello di visualizzazione.

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

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

non ha 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 nelle 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

Puoi migliorare in modo significativo le prestazioni 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top