Frage

Betrachten wir eine Datenbank, Tabellennamen, mit drei Reihen halten:

Peter
Paul
Mary

Gibt es eine einfache Möglichkeit, dies in eine einzige Reihe von Peter, Paul, Mary zu machen?

War es hilfreich?

Lösung

Wenn Sie auf SQL Server 2017 oder Azure sind, finden Sie unter Mathieu Renda Antwort .

Ich hatte ein ähnliches Problem, wenn ich versuche, zwei Tabellen zu verknüpfen mit einer Eins-zu-viele-Beziehungen. In SQL 2005 fand ich, dass XML PATH Methode, um die Verkettung der Zeilen sehr leicht handhaben kann.

Wenn es eine Tabelle mit dem Namen STUDENTS

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Ergebnis ich erwartet war:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

Ich habe folgende 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 in einer kompakteren Weise das gleiche tun, wenn Sie die Kommas am Anfang verketten und verwenden können substring die ersten zu überspringen, so dass Sie eine Unterabfrage nicht tun 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

Andere Tipps

  

Diese Antwort kann unerwartete Ergebnisse zurückkehren für konsistente Ergebnisse, einen der PATH Methoden für XML in anderen Antworten detailliert verwenden.

Verwenden COALESCE:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name 
FROM People

Nur einige Erklärung (da diese Antwort scheint relativ regelmäßig Ansichten zu erhalten):

  • Coalesce ist wirklich nur ein hilfreicher Betrüger, die zwei Dinge erreicht:

1) Keine Notwendigkeit @Names mit einem leeren String-Wert zu initialisieren.

2) Keine Notwendigkeit, eine zusätzliche Trenneinrichtung am Ende abzustreifen.

  • Die Lösung oben wird falsche Ergebnisse geben, wenn eine Zeile a hat NULL Name-Wert (wenn es eine NULL ist, die NULL machen @Names NULL nach dieser Zeile und die nächste Zeile wird wieder als eine leere Zeichenfolge beginnen über feste leicht mit einem von zwei 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, welche von Ihnen gewünschten Verhalten (die erste Option filtert nur NULL s out, die zweite Option, um sie in der Liste mit einer Marker Nachricht hält [ersetzen ‚N / A‘ mit dem, was ist geeignet für Sie]).

Eine Methode noch nicht über den XML data() Befehl in MS SQL Server dargestellt ist:

Angenommen Tabelle namens Namelist mit einer Spalte namens FName,

SELECT FName + ', ' AS 'data()' 
FROM NameList 
FOR XML PATH('')

Rückgabe:

"Peter, Paul, Mary, "

Nur das zusätzliche Komma muss behandelt werden.

Edit: Wie angenommen von @ NReilingh Kommentar, können Sie die folgende Methode verwenden das folgende Komma zu entfernen. Unter der Annahme, die gleichen Tabellen- und Spaltennamen:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

SQL Server 2017+ und SQL Azure: STRING_AGG

Beginnend mit der nächsten Version von SQL Server, können wir endlich über die Reihen verketten, ohne jede Variable oder XML witchery zu müssen.

STRING_AGG (Transact-SQL)

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;

In SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

In SQL Server 2016

Sie können die FÜR JSON-Syntax

d.

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 sich

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

Dies funktioniert sogar Ihre Daten enthält ungültiges XML-Zeichen

die '"},{"_":"' ist sicher, weil, wenn Sie Daten '"},{"_":"', enthalten wird es "},{\"_\":\" entwertet werden

Sie können ', ' mit einer beliebigen Zeichenkette Separator ersetzen


Und in SQL Server 2017 Azure SQL-Datenbank

Sie können den neuen STRING_AGG Funktion

In MySQL gibt es eine Funktion, GROUP_CONCAT () , mit dem Sie die Werte aus mehreren Zeilen verketten ermöglicht. 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

Mit COALESCE - mehr von hier lernen

Ein Beispiel:

  

102

     

103

     

104

Dann unten Code schreiben 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

Postgres-Arrays sind genial. 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 in einem Array:

test=# select array_agg(name) from names;
 array_agg     
------------------- 
 {Peter,Paul,Mary}
(1 row)

Konvertieren Sie das Array in eine kommagetrennte Zeichenfolge:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

Fertig

Seit PostgreSQL 9.0 ist es noch einfacher .

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.

Warnung

Seien Sie vorsichtig, diese Funktion implementiert, wenn es die Möglichkeit des resultierenden String ist über 4000 Zeichen gehen. Es wird eine Ausnahme werfen. Wenn das der Fall ist, dann müssen Sie entweder die Ausnahme behandeln oder Ihre eigene Funktion rollen, die den zusammengefügten Zeichenfolge aus gehen über 4000 Zeichen verhindert.

In SQL Server 2005 und höher, verwenden Sie die Abfrage unter den Zeilen 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

Ich habe keinen Zugriff auf einen SQL Server zu Hause, so dass ich bin Vermutung an der Syntax hier, aber es ist mehr oder weniger:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names

Eine rekursive CTE Lösung wurde vorgeschlagen, aber kein Code zur Verfügung gestellt. Der folgende Code ist ein Beispiel für eine rekursive CTE - beachten Sie, dass, obwohl die Ergebnisse der Frage übereinstimmen, die Daten nicht ganz die angegebenen Beschreibung entspricht, wie ich annehme, dass Sie wirklich wollen, zu tun auf Gruppen von Zeilen, nicht alle Zeilen in der Tabelle. Ändern sie alle Zeilen entsprechen in der Tabelle als eine Übung für den Leser überlassen wird.

;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

Sie müssen eine Variable erstellen, die Ihr endgültiges Ergebnis halten werden, und wählen Sie in sie, wie so.

einfachste Lösung

DECLARE @char VARCHAR(MAX);

SELECT @char = COALESCE(@char + ', ' + [column], [column]) 
FROM [table];

PRINT @char;

Beginnend mit PostgreSQL 9.0 ist dies ganz einfach:

select string_agg(name, ',') 
from names;

In den Versionen vor 9.0 array_agg() verwendet werden kann, wie durch hgmnz gezeigt

In SQL Server vNext wird dies in der STRING_AGG Funktion eingebaut werden, lesen Sie mehr darüber hier: https://msdn.microsoft.com/en-us/library/mt790580.aspx

Mit XML hat mir geholfen, in Reihen mit Komma getrennt bekommen. Für das zusätzliche Komma können wir die Ersetzen-Funktion von SQL Server verwenden. Statt ein Komma der Zugabe, Benutzung des AS ‚Daten ()‘ werden die Zeilen mit Leerzeichen verketten, die später mit Kommas als Syntax ersetzt werden kann, das unten geschrieben wird.

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 

Eine ready-to-use-Lösung, ohne zusätzliches Komma:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

Eine leere Liste wird in NULL-Wert. Normalerweise werden Sie die Liste in einer Tabellenspalte oder Programmvariablen einfügen. Stellen Sie die 255 max Länge, um Ihren Bedarf

(Diwakar und Jens Frandsen zur Verfügung gestellt gute Antworten, aber verbessert werden muss.)

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)

Damit ist die Streu Komma am Anfang.

Wenn Sie jedoch andere Spalten benötigen, oder in der CSV-Kind-Tabelle müssen Sie dies in einem skalare benutzerdefiniertes Feld umwickeln (UDF).

Sie können XML-Pfad als korrelierte Unterabfrage in der SELECT-Klausel verwenden zu (aber ich würde warten müssen, bis ich wieder zur Arbeit gehen, weil Google nicht Arbeit Sachen zu Hause tun: -)

Mit den anderen Antworten, die Person, die Antwort zu lesen muss eine bestimmten Domänentabelle wie Fahrzeug oder Student bewusst sein. Die Tabelle muss mit Daten erstellt und aufgefüllt wird, um eine Lösung zu testen.

Im Folgenden finden Sie ein Beispiel, die SQL Server „INFORMATION_SCHEMA.COLUMNS“ Tabelle verwendet. Durch die Verwendung dieser Lösung müssen keine Tabellen hinzugefügt erstellt oder Daten zu. In diesem Beispiel wird eine durch Kommata getrennte Liste von Spaltennamen für alle Tabellen in der Datenbank.

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 

Für Oracle-DBs finden Sie diese Frage: Wie können, ohne eine gespeicherte Prozedur mehr Zeilen in eine in Oracle verknüpft werden?

Die beste Antwort scheint durch @Emmanuel zu sein, den eingebauten in LISTAGG () -Funktion, in Oracle 11g Release 2 und höher.

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

als @ user762952 wies darauf hin, und nach Oracle-Dokumentation http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php ist die WM_CONCAT () Funktion auch eine Option. Es scheint stabil, aber Oracle empfiehlt ausdrücklich dagegen für jede Anwendung SQL verwenden, dies auf eigene Gefahr.

Other than that, müssen Sie Ihre eigene Funktion schreiben; das Oracle-Dokument oben hat eine Anleitung, wie man das tun.

Ich mochte elegancy von Dana Antwort . Nur wollte es komplett machen.

DECLARE @names VARCHAR(MAX)
SET @names = ''

SELECT @names = @names + ', ' + Name FROM Names 

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)

Um Nullwerte zu vermeiden können Sie verwenden CONCAT ()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

Diese Antwort wird einiges Privileg in Server benötigen zu arbeiten.

Baugruppen eine gute Option für Sie sind. Es gibt eine Menge von Websites, die erklären, wie es zu erstellen. Der, den ich denke, ist sehr gut erklärt ist diese

Wenn Sie wollen, ich habe bereits die Montage erstellt, und es ist möglich, die DLL hier .

Wenn Sie es heruntergeladen haben, müssen Sie das folgende Skript in SQL Server ausgeführt wird:

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 Pfad zur Montage auf dem Server zugegriffen werden kann. Da Sie erfolgreich alle Schritte getan haben, können Sie die Funktion wie verwenden:

SELECT dbo.Concat(field1, ',')
FROM Table1

Hoffe, es hilft !!!

Normalerweise verwende ich wie folgt wählen Zeichenfolgen in SQL Server 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

Wenn Sie mit Nullen beschäftigen möchten, können Sie es tun, indem das Hinzufügen einer where-Klausel oder fügen Sie eine weitere COALESCE um die erste.

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People

MySQL komplettes Beispiel:

Wir haben Benutzer, die viele Datas haben können, und wir wollen eine Ausgabe haben, wo wir alle Benutzer Datas in einer Liste sehen:

Ergebnis:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

Tabelle Setup:

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

In Oracle ist es wm_concat. Ich glaube, diese Funktion in der 10g Release verfügbar ist und höher.

Dies kann 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

Rückkehr

Peter,Paul,Mary

Diese Methode gilt für Teradata Aster Datenbank nur, wie es seine nPath Funktion nutzt.

Auch hier haben wir Tabelle Schüler

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Dann mit nPath es nur einzelne SELECT:

SELECT * FROM npath(
  ON Students
  PARTITION BY SubjectID
  ORDER BY StudentName
  MODE(nonoverlapping)
  PATTERN('A*')
  SYMBOLS(
    'true' as A
  )
  RESULT(
    FIRST(SubjectID of A) as SubjectID,
    ACCUMULATE(StudentName of A) as StudentName
  )
);

Ergebnis:

SubjectID       StudentName
----------      -------------
1               [John, Mary, Sam]
2               [Alaina, Edward]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top