itrain-home Kinderpatenschaften mit Plan Deutschland  
home
 Aktuelle Seite:  knowhow sql tsql pivot pivot2.asp 
 



 

 

 

Produkt Verkäufer Umsatz
Milch Maier 12
Milch Müller 8
Honig Schulze 2
Milch Schulze 34
Butter Maier 17
Butter Müller 2
Honig Maier 19

Produkt  Maier  Müller  Schulze  Summe 
Butter  17 2 0 19
Honig  19 0 2 21
Milch  12 8 34 54
Summe 48 10 36 94

Die Ausgangstabelle

Die SELECT-Spalte

In diesem Beispiel wird die Spalte Produkt als SELECT-Spalte verwendet. Durch die Gruppierung für diese Spalte wird jeder unterschiedliche Wert einmal in einer Zeile aufgeführt.

Die PIVOT-Spalte

Die Spalte Verkäufer wird als Pivot-Spalte verwendet. Für jeden Wert (ohne Duplikate) wird eine Spalte erzeugt. Um dies zu erreichen, muss für jeden eindeutigen Wert eine entsprechende CASE-Anweisung erzeugt werden

Die AGGREGAT-Spalte

Eine numerische Spalte wird verwendet um die Aggregatberechnung durchzuführen. Im Beispiel wird die Summen-Funktion verwendet, aber auch andere Funktionen sind denkbar:

Die AGGREGAT-Funktion

Neben der Summe, können auch der Durchschnit (AVG), das Maximum bzw. Minimum (MAX/MIN) oder die Standardabweichung berechnet werden (STDEV). Um die Prozedur möglichst flexibel zu halten, wird deshalb die zu verwendende Funktion ebenfalls als Parameter übergeben

Eine allgemeine "Kreuztabellen-Prozedur"

Wie kann nun eine allgemeine Kreuztabellen-Prozedur aussehen?

Ein kurzer Blick auf das vorige Beispiel, dieses Mal  mit einigen farblichen Hervorhebungen zeigt die Richtung:

SELECT
    Produkt ,
    SUM(CASE [Verkäufer] WHEN 'Maier' Then Umsatz ELSE 0 END) As 'Maier',
    SUM(CASE [Verkäufer] WHEN 'Müller' Then Umsatz ELSE 0 END) As 'Müller',
    SUM(Umsatz) As Summe
FROM [Verkäufe] GROUP BY Produkt 
WITH CUBE

Die meisten Teile dieser Anweisung können recht einfach zusammengesetzt werden. Der Mittelteil - also die Erzeugung der CASE-Anweisungen - gestaltet sich jedoch etwas schwieriger: Zunächst müssen alle eindeutigen Werte der Pivot-Spalte ermittelt werden und anschließend zu entsprechenden CASE-Anweisungen zusammengesetzt werden.

Was auf den ersten Blick recht kompliziert erscheint, lässt sich mit Transact-SQL recht leicht umsetzen:

Erster Versuch: Ermitteln aller eindeutigen Werte aus der Pivot-Spalte

SELECT DISTINCT [Verkäufer] FROM [Verkäufe]

Ergebnis

verkäufer 
-------------------- 
Maier
Müller
Schulze

Zweiter Versuch: Erstellen einer komma-getrennten Liste aller Verkäufer

DECLARE @Liste varchar(4000)
SET @Liste = ''
SELECT @Liste = @Liste + [Verkäufer] + ',' FROM (SELECT DISTINCT [Verkäufer] FROM [Verkäufe]) V
SELECT @Liste 

Ergebnis:

Maier,Müller,Schulze,

Jetzt ist es nur noch ein kleiner Schritt, bis zur kompletten Erzeugung der CASE-Anweisungen:

DECLARE @Liste varchar(4000)
SET @Liste = ''
SELECT @Liste = 
               @Liste + 
              'SUM (CASE [Verkäufer] WHEN ''' + 
              [Verkäufer] + 
              'THEN Umsatz ELSE 0 END, ' 
              FROM (SELECT DISTINCT [Verkäufer] FROM [Verkäufe]) V
SELECT @Liste 

Das Ergebnis:

SUM (CASE [Verkäufer] WHEN 'Maier' THEN Umsatz ELSE 0 END, 
SUM (CASE [Verkäufer] WHEN 'Müller' THEN Umsatz ELSE 0 END, 
SUM (CASE [Verkäufer] WHEN 'Schulze' THEN Umsatz ELSE 0 END, 

Das Finale

Nachdem die erste Hürde erfolgreich überwunden ist, bleibt noch das Zusammensetzen der gesamten SQL-Anweisung.

Um alles flexibel zu halten, werden die einzelnen Komponenten der Anweisung gleich als Parameter definiert:

DECLARE @Aggregat_Funktion nvarchar(50)
DECLARE @Aggregat_Spalte nvarchar(255)
DECLARE @Ausgangstabelle nvarchar(255)
DECLARE @Select_Spalte nvarchar(255)
DECLARE @Pivot_Spalte nvarchar(255)

Zusätzlich werden noch einige Variablen für die eigentliche SELECT-Anweisung benötigt. In der Variablen @SQL_Anfang soll der einleitende Teil der SELECT-Anweisung abgelegt werden. Die Variable @SQL_Liste wird verwendet, um die Liste der CASE-Anweisungen dynamisch abzufragen. Das Ergebnis wird in @SQL_Mitte abgelegt.

In der Variablen @SQL_Ende werden schließlich alle verbleibenden Anweisungen (Summenbildung, Gruppierung, CUBE) untergebracht.


DECLARE @SQL_Anfang nvarchar(2000)
DECLARE @SQL_Liste nvarchar(4000)
DECLARE @SQL_Mitte nvarchar(4000)
DECLARE @SQL_Ende nvarchar(2000)

Also ans Werk: Zunächst werden die Parameter für unser Beispiel vorbelegt:
SET @Aggregat_Funktion = 'SUM'
SET @Aggregat_Spalte = 'Umsatz'
SET @Ausgangstabelle = '[Verkäufe]'
SET @Select_Spalte = 'Produkt'
SET @Pivot_Spalte = '[Verkäufer]'

Anschließend wird der erste Teil der SELECT-Anweisung dynamisch erzeugt:

(So soll es im Beispiel aussehen: SELECT Produkt, )


SET @SQL_Anfang = 'SELECT ' + @Select_Spalte + ', '

Jetzt kommt der schwierige Part: Wie schon gesehen, kann die Liste der CASE-Anweisungen dynamisch erzeugt werden. Jetzt stellt sich aber als zusätzliches Problem, daß die Spaltennamen ebenfalls dynamisch übergeben werden. Abhilfe kann hier die Prozedur sp_executesql schaffen. Mit dieser Prozedur kann eine dynamisch erzeugte SQL-Anweisung ausgeführt werden. Was für diesen Fall jedoch noch wichtiger ist, ist die Möglichkeit einen Parameter aus der dynamischen erzeugten Anweisung wieder nach aussen zurückzugeben.
Zuerst wird also die eigentliche Anweisung dynamisch erzeugt. 

SET @SQL_Liste = 'SELECT @Liste = ' +
' @Liste + ''' +
@Aggregat_Funktion + ' (CASE ' +
@Pivot_Spalte + ' WHEN '''''' + ' + 
@Pivot_Spalte
' + '''''' THEN ' +
@Aggregat_Spalte + ' ELSE 0 END) AS ' +
@Pivot_Spalte + ', ''' +
' FROM (SELECT DISTINCT ' + @Pivot_Spalte
' FROM ' + @Ausgangstabelle + ') V ' 
Anschließend kann diese Anweisung ausgeführt werden. Die Variable @Liste wird von außen übergeben und auch wieder ausgelesen:

SET @SQL_Mitte = ''
EXEC sp_executesql @sql_liste, N'@Liste nvarchar(4000) OUTPUT', @Liste = @SQL_Mitte OUTPUT

Verbleibt noch das Zusammenbauen der restlichen Anweisungen (Summenbildung für die SELECT-Spalte, Gruppieren nach der SELECT-Spalte und CUBE bilden:

(So soll es aussehen: SUM(Umsatz) As Summe FROM [Verkäufe] GROUP BY Produkt WITH CUBE)

SET @SQL_Ende = @Aggregat_Funktion + '(' + 
@Aggregat_Spalte + ') As Summe ' + 
' FROM ' + @Ausgangstabelle
' GROUP BY ' + @Select_Spalte
' WITH CUBE'

Geschafft! Als letztes werden die drei Anweisungen (Start, Mitte, Ende) zusammengesetzt und ausgeführt:
EXEC (@SQL_Anfang + @SQL_Mitte + @SQL_Ende)

Falls Sie das ganze selbst ausprobieren möchten, laden Sie doch einfach das fertige Script von unserem Server.

War's das?

Wie fast immer im wirklichen Leben gilt es bei realen Problemstellungen noch einige weitere Besonderheiten zu beachten.

In dem gezeigten kleinen Beispiel waren einige Randbedingungen erfüllt, die die Ausführung vereinfacht haben.

So tauchten zunächst in der Aggregat-Spalte keine NULL-Werte auf. Dadurch ist immer ein korrektes Resultat gewährleistet. Falls jedoch Null-Werte auftreten können, müssen diese gesondert behandelt werden. Bei den Werten in der Pivot-Spalte handelte es sich außerdem um Zeichenketten. Testen Sie die kleine Prozedur einmal mit einer numerischen Spalte (z.B. eine ID oder eine Jahreszahl). Der Vergleich in der CASE-Anweisung wird scheitern, da die Datentypen für den Vergleich nicht übereinstimmen.

Die folgende gespeicherte Prozedur sp_Transform versucht diese Eventualitäten weitestgehend abzufangen. Testen Sie die Prozedur doch einmal. Falls Sie auf Probleme stossen, freuen wird uns über ein Feedback. (Natürlich auch, wenn Ihnen die Prozedur weiterhilft.)

Wie geht es weiter?

Die gezeigte Prozedur hilft sicherlich einige kleinere Problemstellungen relativ schnell zu lösen. Sollen die Gruppierungen jedoch auf mehreren Stufen erfolgen, sollten Sie auf jeden Fall einen Blick auf Microsofts OLAP-Server werfen. Der OLAP-Server wird zusammen mit dem SQL Server ausgeliefert. Im Gegensatz zu einer dynamischen Abfrage (wie mit sp_Transform) werden hier die Daten jedoch zunächst aufbereitet. Für unseren Fall würde das heißen, das zunächst die Dimensionen aufbereitet werden (Alle DISTINCT Werte aus Produkte und Verkäufer). Anschließend werden aus der eigentlichen Faktentabelle alle beötigten Zwischensummen berechnet und ebenfalls abgespeichert. Die Abfrage darüber ist natürlich wesentlich schneller, da kaum noch Berechnungen ausgeführt werden müssen und keine gleichzeitigen Aktualisierungen an den Daten vorgenommen werden.

Leerraum

Dokument zum Drucken anzeigen
English Pages
Link-Tipp zum Thema "Certification": www.geocities.com/siliconvalley/haven/8823/mcse1.htm

Umfangreiche Informationen rund um die Zertifizierung.