Comment puis-je obtenir:
id Name Value
1 A 4
1 B 8
2 C 9
à
id Column
1 A:4, B:8
2 C:9
Comment puis-je obtenir:
id Name Value
1 A 4
1 B 8
2 C 9
à
id Column
1 A:4, B:8
2 C:9
Réponses:
Aucun curseur, boucle WHILE ou fonction définie par l'utilisateur n'est nécessaire .
Il suffit d'être créatif avec FOR XML et PATH.
[Remarque: Cette solution ne fonctionne que sur SQL 2005 et versions ultérieures. La question d'origine ne spécifiait pas la version utilisée.]
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
S'il s'agit de SQL Server 2017 ou SQL Server Vnext, SQL Azure, vous pouvez utiliser string_agg comme ci-dessous:
select id, string_agg(concat(name, ':', [value]), ', ')
from #YourTable
group by id
l'utilisation du chemin XML ne concaténera pas parfaitement comme vous pouvez vous y attendre ... il remplacera "&" par "& amp;" et va également jouer avec <" and ">
... peut-être quelques autres choses, je ne sais pas ... mais vous pouvez essayer cela
Je suis tombé sur une solution de contournement pour cela ... vous devez remplacer:
FOR XML PATH('')
)
avec:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
... ou NVARCHAR(MAX)
si c'est ce que vous utilisez.
pourquoi l'enfer n'a pas de SQL
fonction d'agrégation concaténée? c'est un PITA.
Je suis tombé sur quelques problèmes quand j'essayé de convertir la suggestion de Kevin Fairchild à travailler avec des chaînes contenant des espaces et des caractères spéciaux (XML &
, <
, >
) qui ont été encodées.
La version finale de mon code (qui ne répond pas à la question d'origine mais peut être utile à quelqu'un) ressemble à ceci:
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
Plutôt que d'utiliser un espace comme délimiteur et de remplacer tous les espaces par des virgules, il ajoute simplement une virgule et un espace à chaque valeur, puis les utilise STUFF
pour supprimer les deux premiers caractères.
Le codage XML est pris en charge automatiquement à l'aide de la directive TYPE .
Une autre option utilisant Sql Server 2005 et supérieur
---- 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
Installez les agrégats SQLCLR à partir de http://groupconcat.codeplex.com
Ensuite, vous pouvez écrire du code comme celui-ci pour obtenir le résultat que vous avez demandé:
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 et versions ultérieures vous permettent de créer vos propres fonctions d'agrégation personnalisées , y compris pour des choses comme la concaténation - voir l'exemple au bas de l'article lié.
Huit ans plus tard ... Le moteur de base de données Microsoft SQL Server vNext a enfin amélioré Transact-SQL pour prendre directement en charge la concaténation de chaînes groupées. La version 1.0 de Community Technical Preview a ajouté la fonction STRING_AGG et CTP 1.1 a ajouté la clause WITHIN GROUP pour la fonction STRING_AGG.
Référence: https://msdn.microsoft.com/en-us/library/mt775028.aspx
Ceci n'est qu'un ajout au post de Kevin Fairchild (très intelligent d'ailleurs). Je l'aurais ajouté en commentaire, mais je n'ai pas encore assez de points :)
J'utilisais cette idée pour une vue sur laquelle je travaillais, mais les éléments que je concaténais contenaient des espaces. J'ai donc légèrement modifié le code pour ne pas utiliser d'espaces comme délimiteurs.
Encore merci pour la solution de contournement cool 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
Dans Oracle, vous pouvez utiliser la fonction d'agrégation LISTAGG.
Disques originaux
name type
------------
name1 type1
name2 type2
name2 type3
Sql
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
Aboutir à
name type
------------
name1 type1
name2 type2; type3
Ce type de question est très souvent posé ici, et la solution va dépendre beaucoup des exigences sous-jacentes:
https://stackoverflow.com/search?q=sql+pivot
et
https://stackoverflow.com/search?q=sql+concatenate
En règle générale, il n'existe aucun moyen SQL uniquement de le faire sans SQL dynamique, une fonction définie par l'utilisateur ou un curseur.
Juste pour ajouter à ce que Cade a dit, il s'agit généralement d'un affichage frontal et devrait donc y être géré. Je sais qu'il est parfois plus facile d'écrire quelque chose à 100% en SQL pour des choses comme l'exportation de fichiers ou d'autres solutions "SQL uniquement", mais la plupart du temps cette concaténation doit être gérée dans votre couche d'affichage.
Pas besoin de curseur ... une boucle while est suffisante.
------------------------------
-- 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
Soyons très simples:
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
Remplacez cette ligne:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
Avec votre requête.
n'a pas vu de réponses d'application croisée, également pas besoin d'extraction xml. Voici une version légèrement différente de ce que Kevin Fairchild a écrit. C'est plus rapide et plus facile à utiliser dans des requêtes plus complexes:
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
Vous pouvez améliorer les performances de manière significative si le regroupement par contient principalement un élément:
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
Utilisation de la fonction Remplacer et POUR LE CHEMIN JSON
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
Pour des exemples de données et d'autres moyens, cliquez ici
Si vous avez activé clr, vous pouvez utiliser la bibliothèque Group_Concat de GitHub
GROUP_CONCAT()
fonction d'agrégation, mais le résoudre sur Microsoft SQL Server est plus délicat. Voir la question SO suivante pour obtenir de l'aide: " Comment obtenir plusieurs enregistrements contre un enregistrement basé sur la relation? "