Wie bekomme ich:
id Name Value
1 A 4
1 B 8
2 C 9
zu
id Column
1 A:4, B:8
2 C:9
Wie bekomme ich:
id Name Value
1 A 4
1 B 8
2 C 9
zu
id Column
1 A:4, B:8
2 C:9
Antworten:
Kein CURSOR, WHILE-Loop oder benutzerdefinierte Funktion erforderlich .
Sie müssen nur mit FOR XML und PATH kreativ sein.
[Hinweis: Diese Lösung funktioniert nur unter SQL 2005 und höher. In der ursprünglichen Frage wurde die verwendete Version nicht angegeben.]
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
Wenn es sich um SQL Server 2017 oder SQL Server Vnext, SQL Azure handelt, können Sie string_agg wie folgt verwenden:
select id, string_agg(concat(name, ':', [value]), ', ')
from #YourTable
group by id
Die Verwendung des XML-Pfads wird nicht wie erwartet perfekt verkettet. Er ersetzt "&" durch "& amp;" und wird auch mit <" and ">
... vielleicht ein paar anderen Dingen herumspielen, nicht sicher ... aber Sie können dies versuchen
Ich bin auf eine Problemumgehung gestoßen ... Sie müssen Folgendes ersetzen:
FOR XML PATH('')
)
mit:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
... oder NVARCHAR(MAX)
wenn du das benutzt.
Warum zum Teufel hat keine SQL
verkettete Aggregatfunktion? Das ist eine PITA.
Ich lief in ein paar Probleme , als ich versuchte Kevin Fairchilds Vorschlag zur Arbeit Umwandlung mit Streichern Leer- und Sonder XML - Zeichen enthalten ( &
, <
, >
) , die codiert wurden.
Die endgültige Version meines Codes (die die ursprüngliche Frage nicht beantwortet, aber für jemanden nützlich sein kann) sieht folgendermaßen aus:
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
Anstatt ein Leerzeichen als Trennzeichen zu verwenden und alle Leerzeichen durch Kommas zu ersetzen, werden jedem Wert nur ein Komma und ein Leerzeichen vorangestellt und dann STUFF
die ersten beiden Zeichen entfernt.
Die XML-Codierung wird automatisch mithilfe der TYPE- Direktive durchgeführt.
Eine weitere Option mit SQL Server 2005 und höher
---- 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
Installieren Sie die SQLCLR-Aggregate von http://groupconcat.codeplex.com
Dann können Sie Code wie diesen schreiben, um das gewünschte Ergebnis zu erhalten:
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;
Mit SQL Server 2005 und höher können Sie Ihre eigenen benutzerdefinierten Aggregatfunktionen erstellen , einschließlich Informationen zur Verkettung. Weitere Informationen finden Sie im Beispiel am Ende des verknüpften Artikels.
Acht Jahre später ... Microsoft SQL Server vNext Database Engine hat Transact-SQL endlich erweitert, um die Verkettung gruppierter Zeichenfolgen direkt zu unterstützen. In der Community Technical Preview Version 1.0 wurde die Funktion STRING_AGG und in CTP 1.1 die Klausel WITHIN GROUP für die Funktion STRING_AGG hinzugefügt.
Referenz: https://msdn.microsoft.com/en-us/library/mt775028.aspx
Dies ist nur eine Ergänzung zu Kevin Fairchilds Beitrag (übrigens sehr klug). Ich hätte es als Kommentar hinzugefügt, aber ich habe noch nicht genug Punkte :)
Ich benutzte diese Idee für eine Ansicht, an der ich arbeitete, aber die Gegenstände, die ich konzentrierte, enthielten Leerzeichen. Deshalb habe ich den Code leicht geändert, um keine Leerzeichen als Trennzeichen zu verwenden.
Nochmals vielen Dank für die coole Problemumgehung 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 können Sie die LISTAGG-Aggregatfunktion verwenden.
Originalaufzeichnungen
name type
------------
name1 type1
name2 type2
name2 type3
Sql
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
Ergebnis in
name type
------------
name1 type1
name2 type2; type3
Diese Art von Frage wird hier sehr oft gestellt, und die Lösung wird stark von den zugrunde liegenden Anforderungen abhängen:
https://stackoverflow.com/search?q=sql+pivot
und
https://stackoverflow.com/search?q=sql+concatenate
Normalerweise gibt es keine reine SQL-Möglichkeit, dies ohne dynamisches SQL, eine benutzerdefinierte Funktion oder einen Cursor zu tun.
Nur um das zu ergänzen, was Cade gesagt hat, ist dies normalerweise eine Front-End-Anzeige und sollte daher dort behandelt werden. Ich weiß, dass es manchmal einfacher ist, etwas 100% in SQL für Dinge wie den Dateiexport oder andere "Nur SQL" -Lösungen zu schreiben, aber meistens sollte diese Verkettung in Ihrer Anzeigeebene behandelt werden.
Benötigen Sie keinen Cursor ... eine while-Schleife ist ausreichend.
------------------------------
-- 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
Lassen Sie uns ganz einfach werden:
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
Ersetzen Sie diese Zeile:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
Mit Ihrer Anfrage.
Es wurden keine Kreuzantworten angezeigt, und es ist auch keine XML-Extraktion erforderlich. Hier ist eine etwas andere Version dessen, was Kevin Fairchild geschrieben hat. Es ist schneller und einfacher in komplexeren Abfragen zu verwenden:
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
Sie können die Leistung auf folgende Weise erheblich verbessern, wenn die Gruppierung nach hauptsächlich ein Element enthält:
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
Verwenden der Ersetzungsfunktion und des FOR JSON-Pfads
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
Für Beispieldaten und weitere Möglichkeiten klicken Sie hier
Wenn Sie clr aktiviert haben, können Sie die Group_Concat- Bibliothek von GitHub verwenden
GROUP_CONCAT()
Aggregatfunktion leicht lösen , aber das Lösen unter Microsoft SQL Server ist umständlicher. Siehe die folgende SO-Frage für Hilfe: " Wie bekomme ich mehrere Datensätze gegen einen Datensatz basierend auf der Beziehung? "