Spaltenwerte als kommaseparierte Liste zurückgeben

By Frank Kalis

Posted on Sep 21, 2004 von in SQL Server

Wie so oft mag man sich fragen, ob dies eher die Aufgabe des Clients als die des Server ist, aber da man häufig derartige Fragestellungen beobachten kann, hier an dieser Stelle vielleicht ein paar Lösungsansätze zu folgendem Problem:

Gegeben ist eine Tabelle note mit einer Spalte col1.

CREATE TABLE note
(
	col1 VARCHAR(8)
)

So, in dieser Tabelle stehen jetzt beispielsweise folgende Daten:

INSERT INTO note (col1) VALUES('Notes1')
INSERT INTO note (col1) VALUES('Notes2')
INSERT INTO note (col1) VALUES('Notes3')
INSERT INTO note (col1) VALUES('Notes4')
INSERT INTO note (col1) VALUES('Notes5')
INSERT INTO note (col1) VALUES('Notes6')
INSERT INTO note (col1) VALUES('Notes7')
INSERT INTO note (col1) VALUES('Notes8')
INSERT INTO note (col1) VALUES('Notes9')
INSERT INTO note (col1) VALUES('Notes10')

Mit einem

SELECT
	col1
FROM
	note

erhält man

col1     
-------- 
Notes1
Notes2
Notes3
Notes4
Notes5
Notes6
Notes7
Notes8
Notes9
Notes10

Gewünscht aber ist folgenden Resultat:

Notes1, Notes2, Notes3, Notes4, Notes5, Notes6, Notes7, Notes8, Notes9, Notes10

Hm, genau hier könnte man nun die Argumentation einsetzen, daß dies eher eine Präsentationssache ist und damit von Client geregelt werden sollte. Wie aber würde man dies mit T-SQL lösen?

Alternative 1: Der Cursor

DECLARE @tmp VARCHAR(8)
DECLARE @result VARCHAR(8000)
DECLARE stupid_cursor CURSOR FOR
	SELECT col1 FROM note
OPEN stupid_cursor
FETCH NEXT FROM stupid_cursor INTO @tmp
WHILE @@FETCH_STATUS = 0
BEGIN
	SET @result = COALESCE(@result + ', ','') + @tmp
	FETCH NEXT FROM stupid_cursor into @tmp
END
CLOSE stupid_cursor
DEALLOCATE stupid_cursor
SELECT @result
GO

                                                                                
-------------------------------------------------------------------------------
Notes1, Notes2, Notes3, Notes4, Notes5, Notes6, Notes7, Notes8, Notes9, Notes10

(1 row(s) affected)

Das Ergebnis ist zwar das Gewünschte, aber ein Cursor ist nicht gerade etwas, was man häufig verwenden sollte. Also muß es etwas besseres geben.

Alternative 2: Die UDF

--UDF Version
CREATE FUNCTION dbo.flattentable()
RETURNS VARCHAR(8000)
AS
	BEGIN
	DECLARE @MyString VARCHAR(8000)
		SELECT 
			@MyString = ISNULL( @MyString + ', ', '' ) + col1 
		FROM 
			note
	RETURN @MyString
	END
GO
SELECT dbo.flattentable()

-------------------------------------------------------------------------------
Notes1, Notes2, Notes3, Notes4, Notes5, Notes6, Notes7, Notes8, Notes9, Notes10

(1 row(s) affected)

Auch hier ist das Ergebnis das, was wir haben wollten. Leider sind solche skalaren Funktionen im SQL Server 2000 nicht viel besser als ein Cursor. Auch sie werden Zeile für Zeile angearbeitet und können gerade bei großen Datenmengen wahre Performancekiller sein. Natürlich könnte man auch eine UDF erstellen, die eine table Variable zurückgibt. Diese werden intern ähnlich wie Views gehandhabt. Aber wir suchen mal weiter.

Alternative 3 + 4+5: Die Setbasierten Lösungen

--Set Version 1
DECLARE @allnotes VARCHAR(8000)
SELECT 
	@allnotes = ISNULL( @allnotes + ', ', '' ) + col1 
FROM 
	note
SELECT 
	@allnotes

--Set Version 2
DECLARE @List VARCHAR(8000)
SET @List = ''
SELECT @List = @List + ', ' + Col1
FROM Note
SELECT STUFF(@List,1,2,'')

--Set Version 3
DECLARE @MyString VARCHAR(100)
SET @MyString = ''
SELECT
    @MyString = @MyString + col1+', '
FROM
    note
SELECT
    LEFT(@MyString, LEN (@MyString)-1)
                                                                                
-------------------------------------------------------------------------------
Notes1, Notes2, Notes3, Notes4, Notes5, Notes6, Notes7, Notes8, Notes9, Notes10

(1 row(s) affected)

Die Set Version 1 habe ich zum ersten Mal in einem Posting von Twan van Beers bemerkt. obwohl sie anscheinend durchaus gebräuchlich ist. Thank you, Twan!

Die Set Version 2 stammt von Jonathan van Houtte (I owe you that much !!! ), während die dritte der Vollständigkeithalber von mir hinzugefügt wurde.

Setbasierte Lösungen sind die Stärken des SQL Servers und sollten stets bevorzugt werden. Anzumerken ist noch, daß die Rückgabewerte vom Typ VARCHAR(8000) sind. Alles, was darüber hinausgeht, wird abgeschnitten. Ebenfalls anmerken sollte man, daß gerade bei großen Tabellen die Verwendung von temporären Tabellen bessere Performance zeigt als dieser Verknüpfungstrick.

Dieser Eintrag wurde eingetragen von und ist abgelegt unter SQL Server. Tags: , , , ,

Noch kein Feedback


Formular wird geladen...