By Frank Kalis
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.