Stellen Sie sich eine Datenbanktabelle mit Namen und drei Zeilen vor:
Peter
Paul
Mary
Gibt es eine einfache Möglichkeit, daraus eine einzelne Zeichenfolge zu machen Peter, Paul, Mary
?
Stellen Sie sich eine Datenbanktabelle mit Namen und drei Zeilen vor:
Peter
Paul
Mary
Gibt es eine einfache Möglichkeit, daraus eine einzelne Zeichenfolge zu machen Peter, Paul, Mary
?
Antworten:
Wenn Sie mit SQL Server 2017 oder Azure arbeiten, lesen Sie die Antwort von Mathieu Renda .
Ich hatte ein ähnliches Problem, als ich versuchte, zwei Tabellen mit Eins-zu-Viele-Beziehungen zu verbinden. In SQL 2005 habe ich festgestellt, dass diese XML PATH
Methode die Verkettung der Zeilen sehr einfach handhaben kann.
Wenn eine Tabelle aufgerufen wird STUDENTS
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
Ergebnis, das ich erwartet hatte, war:
SubjectID StudentName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
Ich habe folgendes verwendet 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]
Sie können dasselbe auf kompaktere Weise tun, wenn Sie die Kommas am Anfang zusammenfassen und substring
das erste überspringen können, damit Sie keine Unterabfrage durchführen müssen:
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
<
oder enthalten &
. Siehe @ BenHinmans Kommentar.
FOR XML PATH ('')
. Das bedeutet, dass es nicht als zuverlässig angesehen werden sollte, da Patches oder Updates die Funktionsweise ändern könnten. Es basiert im Grunde auf einer veralteten Funktion.
FOR XML
soll XML generiert und keine beliebigen Zeichenfolgen verkettet werden. Deshalb ist es entweicht &
, <
und >
auf XML - Entity - Codes ( &
, <
, >
). Ich gehe davon aus, dass es auch entkommen wird "
und '
zu "
und '
in Attributen. Es ist nicht GROUP_CONCAT()
, string_agg()
, array_agg()
, listagg()
, usw. , auch wenn Sie Art macht es tun. Wir sollten unsere Zeit damit verbringen, von Microsoft die Implementierung einer ordnungsgemäßen Funktion zu fordern.
string_agg
in v.Next hinzufügen . und all dies kann verschwinden.
Diese Antwort kann zu unerwarteten Ergebnissen führen. Verwenden Sie für konsistente Ergebnisse eine der in anderen Antworten beschriebenen FOR XML PATH-Methoden.
Verwendung COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
Nur eine Erklärung (da diese Antwort relativ regelmäßige Ansichten zu bekommen scheint):
1) Keine Notwendigkeit, @Names
mit einem leeren Zeichenfolgenwert zu initialisieren .
2) Am Ende muss kein zusätzlicher Abscheider entfernt werden.
@Names
NULL nach dieser Zeile und die nächste Zeile wird wieder als eine leere Zeichenfolge beginnen. Einfach mit einem von zwei festen Lösungen:DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL
oder:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') +
ISNULL(Name, 'N/A')
FROM People
Je nachdem, welches Verhalten Sie möchten (die erste Option filtert nur NULL- Werte heraus, die zweite Option hält sie in der Liste mit einer Markierungsnachricht [ersetzen Sie 'N / A' durch die für Sie geeignete]).
Ab der nächsten Version von SQL Server können wir endlich zeilenübergreifend verketten, ohne auf Variablen oder XML-Hexerei zurückgreifen zu müssen.
Ohne Gruppierung
SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;
Mit Gruppierung:
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Mit Gruppierung und Untersortierung
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
Eine Methode, die über den XML
data()
Befehl in MS SQL Server noch nicht angezeigt wird, ist:
Angenommen, die Tabelle heißt NameList mit einer Spalte namens FName.
SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')
kehrt zurück:
"Peter, Paul, Mary, "
Es muss nur das zusätzliche Komma behandelt werden.
Bearbeiten: Wie aus @ NReilinghs Kommentar übernommen, können Sie das folgende Komma mit der folgenden Methode entfernen. Unter der Annahme der gleichen Tabellen- und Spaltennamen:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
+ ', '
zwischen jedem verketteten Element immer noch ein Leerzeichen eingefügt wird.
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'')
Sie können die FOR JSON-Syntax verwenden
dh
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
Und das Ergebnis wird
Id Emails
1 abc@gmail.com
2 NULL
3 def@gmail.com, xyz@gmail.com
Dies funktioniert auch dann, wenn Ihre Daten ungültige XML-Zeichen enthalten
Das '"},{"_":"'
ist sicher, denn wenn Sie Daten enthalten '"},{"_":"',
, werden diese maskiert"},{\"_\":\"
Sie können durch ein ', '
beliebiges String-Trennzeichen ersetzen
Sie können die neue Funktion STRING_AGG verwenden
<
, >
, &
usw. , die FOR XML PATH('')
automatisch entweicht.
In MySQL gibt es eine Funktion, GROUP_CONCAT () , mit der Sie die Werte aus mehreren Zeilen verketten können. Beispiel:
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 '", "'
werde ich am Ende des letzten Eintrags einige Zeichen vermissen. Warum kann das passieren?
CHAR
, müssen Sie sie umwandeln , z. B. über GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2) Wenn viele Werte kommen, sollten Sie group_concat_max_len
die in stackoverflow.com/a/1278210/1498405
Verwenden Sie COALESCE - Erfahren Sie hier mehr
Zum Beispiel:
102
103
104
Dann schreiben Sie unten Code in SQL Server,
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
Ausgabe wäre:
102,103,104
Declare @Numbers AS Nvarchar(MAX)
und es hat gut funktioniert. Können Sie bitte erklären, warum Sie empfehlen, es nicht zu verwenden?
Postgres-Arrays sind fantastisch. Beispiel:
Erstellen Sie einige Testdaten:
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)
Aggregieren Sie sie in einem Array:
test=# select array_agg(name) from names;
array_agg
-------------------
{Peter,Paul,Mary}
(1 row)
Konvertieren Sie das Array in eine durch Kommas getrennte Zeichenfolge:
test=# select array_to_string(array_agg(name), ', ') from names;
array_to_string
-------------------
Peter, Paul, Mary
(1 row)
ERLEDIGT
Seit PostgreSQL 9.0 ist es noch einfacher .
select array_to_string(array_agg(name||'('||id||')'
Oracle 11g Release 2 unterstützt die LISTAGG-Funktion. Dokumentation hier .
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.
Seien Sie vorsichtig bei der Implementierung dieser Funktion, wenn die Möglichkeit besteht, dass die resultierende Zeichenfolge mehr als 4000 Zeichen umfasst. Es wird eine Ausnahme ausgelöst. Wenn dies der Fall ist, müssen Sie entweder die Ausnahme behandeln oder Ihre eigene Funktion ausführen, die verhindert, dass die verknüpfte Zeichenfolge mehr als 4000 Zeichen enthält.
LISTAGG
funktioniert perfekt! Lesen Sie einfach das hier verlinkte Dokument. wm_concat
ab Version 12c entfernt.
Verwenden Sie in SQL Server 2005 und höher die folgende Abfrage, um die Zeilen zu verketten.
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
<
oder enthalten &
.
Ich habe zu Hause keinen Zugriff auf einen SQL Server, daher schätze ich die Syntax hier, aber es ist mehr oder weniger:
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, '')
Eine rekursive CTE-Lösung wurde vorgeschlagen, es wurde jedoch kein Code bereitgestellt. Der folgende Code ist ein Beispiel für einen rekursiven CTE. Beachten Sie, dass die Ergebnisse zwar mit der Frage übereinstimmen, die Daten jedoch nicht ganz mit der angegebenen Beschreibung übereinstimmen, da ich davon ausgehe, dass Sie dies wirklich für Zeilengruppen tun möchten, nicht für alle Zeilen in der Tabelle. Das Ändern so, dass es mit allen Zeilen in der Tabelle übereinstimmt, bleibt dem Leser als Übung.
;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
Spalte dann in eine durch Kommas getrennte Zeichenfolge für 4 Gruppen von id
s. Auf den ersten Blick denke ich, dass dies mehr Arbeit ist als die meisten anderen Lösungen für SQL Server.
Ab PostgreSQL 9.0 ist dies ganz einfach:
select string_agg(name, ',')
from names;
In Versionen vor 9.0 array_agg()
kann wie von hgmnz gezeigt verwendet werden
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
oderchar
In SQL Server vNext wird dies mit der Funktion STRING_AGG integriert. Weitere Informationen finden Sie hier: https://msdn.microsoft.com/en-us/library/mt790580.aspx
Die Verwendung von XML hat mir geholfen, Zeilen durch Kommas zu trennen. Für das zusätzliche Komma können wir die Ersetzungsfunktion von SQL Server verwenden. Anstatt ein Komma hinzuzufügen, verkettet die Verwendung des AS 'data ()' die Zeilen mit Leerzeichen, die später durch Kommas wie die unten beschriebene Syntax ersetzt werden können.
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
Eine gebrauchsfertige Lösung ohne zusätzliche Kommas:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
Eine leere Liste führt zu einem NULL-Wert. Normalerweise fügen Sie die Liste in eine Tabellenspalte oder Programmvariable ein: Passen Sie die maximale Länge von 255 an Ihre Bedürfnisse an.
(Diwakar und Jens Frandsen gaben gute Antworten, müssen aber verbessert werden.)
', '
mit , ','
wenn Sie nicht den zusätzlichen Platz wollen.
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')
Hier ist ein Beispiel:
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)
Dies setzt das Streukomma an den Anfang.
Wenn Sie jedoch andere Spalten benötigen oder eine untergeordnete Tabelle CSV-fähig machen möchten, müssen Sie diese in ein skalares benutzerdefiniertes Feld (UDF) einschließen.
Sie können den XML-Pfad auch als korrelierte Unterabfrage in der SELECT-Klausel verwenden (aber ich muss warten, bis ich wieder zur Arbeit gehe, da Google zu Hause keine Arbeit erledigt :-)
Bei den anderen Antworten muss die Person, die die Antwort liest, eine bestimmte Domänentabelle wie Fahrzeug oder Schüler kennen. Die Tabelle muss erstellt und mit Daten gefüllt werden, um eine Lösung zu testen.
Unten finden Sie ein Beispiel, das die SQL Server-Tabelle "Information_Schema.Columns" verwendet. Mit dieser Lösung müssen keine Tabellen erstellt oder Daten hinzugefügt werden. In diesem Beispiel wird eine durch Kommas getrennte Liste von Spaltennamen für alle Tabellen in der Datenbank erstellt.
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
Informationen zu Oracle-DBs finden Sie in dieser Frage: Wie können in Oracle mehrere Zeilen zu einer verknüpft werden, ohne dass eine gespeicherte Prozedur erstellt wird?
Die beste Antwort scheint @Emmanuel zu sein, der die integrierte Funktion LISTAGG () verwendet, die in Oracle 11g Release 2 und höher verfügbar ist.
SELECT question_id,
LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
Wie @ user762952 hervorhob und laut Oracle-Dokumentation http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , ist die Funktion WM_CONCAT () ebenfalls eine Option. Es scheint stabil zu sein, aber Oracle empfiehlt ausdrücklich, es nicht für SQL-Anwendungen zu verwenden. Verwenden Sie es daher auf eigenes Risiko.
Ansonsten müssen Sie Ihre eigene Funktion schreiben. Das obige Oracle-Dokument enthält eine Anleitung dazu.
Um Nullwerte zu vermeiden, können Sie CONCAT () verwenden.
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
Ich mochte die Eleganz von Danas Antwort sehr . Ich wollte es nur vervollständigen.
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
dann müssen Sie sie danach nicht mehr abschneiden.
Diese Antwort erfordert einige Berechtigungen auf dem Server, um zu funktionieren.
Baugruppen sind eine gute Option für Sie. Es gibt viele Websites, die erklären, wie man es erstellt. Der, den ich denke , ist sehr gut erklärt ist dies ein
Wenn Sie möchten, habe ich die Assembly bereits erstellt und Sie können die DLL hier herunterladen .
Nachdem Sie es heruntergeladen haben, müssen Sie das folgende Skript in Ihrem SQL Server ausführen:
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
Beachten Sie, dass der Server möglicherweise auf den Pfad zur Assembly zugreifen kann. Da Sie alle Schritte erfolgreich ausgeführt haben, können Sie die folgende Funktion verwenden:
SELECT dbo.Concat(field1, ',')
FROM Table1
Ich hoffe es hilft!!!
MySQL komplett Beispiel:
Wir haben Benutzer, die viele Daten haben können, und wir möchten eine Ausgabe haben, in der wir alle Benutzerdaten in einer Liste sehen können:
Ergebnis:
___________________________
| id | rowList |
|-------------------------|
| 0 | 6, 9 |
| 1 | 1,2,3,4,5,7,8,1 |
|_________________________|
Tabelleneinrichtung:
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);
Abfrage:
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
Normalerweise verwende ich select wie folgt, um Zeichenfolgen in SQL Server zu verketten:
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
Das hat bei mir funktioniert ( SqlServer 2016 ):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
Hier ist die Quelle: https://www.mytecbits.com/
Und eine Lösung für MySql (da diese Seite in Google für MySql angezeigt wird)
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
In Oracle ist es wm_concat
. Ich glaube, diese Funktion ist in der 10g-Version und höher verfügbar .
Dies kann auch nützlich sein
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
kehrt zurück
Peter,Paul,Mary