Die Unterstützung von SQL/OLAP im SQL

Werbung
Fachthema
Unterstützung von SQL/OLAP
DUŠAN PETKOVIĆ
Die Unterstützung von SQL/OLAP im SQLStandard und in relationalen Datenbanksystemen
Die Datenbanksprache SQL war von Anfang an so konzipiert,
dass sie sich besonders gut für die Verarbeitung von operationalen Unternehmensdaten eignete. In den letzten Jahren ist
ein weiteres Anwendungsgebiet für die SQL-Sprache dazugekommen: Die komplexe Analyse von Daten, die nicht in Clientanwendungen umständlich durchgeführt werden soll, sondern direkt vom Datenbankserver unterstützt wird. Für diese
Art von Datenverarbeitung war die SQL-Sprache ursprünglich
nicht besonders gut geeignet. Aus diesem Grund wurde im
ISO-Gremium ein Vorschlag für die Erweiterung von SQL um
analytische Operationen und Funktionen unter dem Namen
SQL/OLAP verabschiedet. Die heutigen relationalen Datenbanksysteme unterstützen die vorgeschlagenen Konzepte von
SQL/OLAP. Zusätzlich dazu hat jeder Datenbankhersteller seine eigenen SQL-Erweiterungen für die komplexe Analyse von
Daten.
Der erste Teil des Beitrags gibt einen Überblick über die im
SQL-Standard vorgeschlagenen Operationen und Funktionen
für die komplexe Analyse. Der zweite Teil des Beitrags, der in
der November-Ausgabe des Datenbank-Spektrums (Heft 23)
erscheinen wird, zeigt, inwieweit die vorgeschlagenen Konzepte in den wichtigsten kommerziellen relationalen Datenbanksystemen realisiert sind. Zusätzlich werden die jeweiligen
nicht standardisierten SQL/OLAP-Erweiterungen in Oracle,
IBM DB2 und Microsoft SQL Server betrachtet.
1 Einleitung
SQL/OLAP entstand im Jahre 1999. Zu Beginn dieses Jahres haben Oracle und IBM gemeinsam dem SQL-Normierungsgremium einen Vorschlag unterbreitet, wie die Erweiterungen in
Bezug auf analytische Funktionen aussehen sollten. IBM hat
Teile dieser Spezifikation schon Mitte desselben Jahres in
DB2 UDB V6.2 implementiert. Oracle Version 8i und DB2 UDB
Version 7.1 haben beide signifikante Teile des Vorschlags enthalten.
Auch weitere Hersteller waren in diesen Prozess involviert: Besonders die Hersteller von Clientanwendungen, wie MicroStrategy, Brio und Cognos, fanden die Idee, die Datenanalyse aus
(ihren) Clientanwendungen in den Datenbankserver zu verlagern,
vielversprechend. (Der generelle Nachteil der Datenverarbeitung
im Client liegt darin, dass unter Umständen eine sehr große Menge von Daten aus dem Server in den Client bewegt werden muss,
damit sie bearbeitet werden kann.)
Die wichtigsten Personen, die sowohl an der Entstehung der Idee
als auch an der Spezifikation mitgewirkt haben, waren Dr. Hamid
Pirahesh von IBM und Andy Witkowski von Oracle. Dr. Pirahesh
hat im Jahre 1998 mit seinem Team angefangen, die Erweiterun44
Datenbank-Spektrum 22/2007
gen in SQL in Bezug auf analytische Funktionen zu untersuchen.
Zum selben Zeitpunkt hat Oracle ein ähnliches Projekt gestartet.
Die beiden Datenbankhersteller haben von den Arbeiten der jeweils anderen Firma erfahren und danach entschieden, zusammen
vorzugehen. Auf der Basis der bereits erfolgten Studien haben die
beiden Firmen in sehr kurzer Zeit eine gemeinsame Spezifikation
erstellt. Dieser Vorschlag wurde mit den führenden Herstellern
von Clientapplikationen diskutiert und anschließend dem ANSIKomitee vorgelegt. Im Laufe desselben Jahres wurde unter der
Leitung von Jim Melton das Projekt intensiv weitergeführt und
abschließend im Jahre 2000 als Änderungsantrag (amendment)
zu SQL:1999-Standard verabschiedet [ISO/IEC 13294 2001].
(Durch das Hinzufügen von SQL/OLAP als Änderungsantrag war
es den Mitgliedern des Standardisierungsgremiums möglich,
SQL/OLAP als Teil des Standards im Jahre 2000 zu veröffentlichen, ohne die Verabschiedung des Standards ein Jahr davor zu
behindern.) Im aktuellen SQL-Standard (SQL:2003) befindet sich
die Spezifikation einzelner OLAP-Operationen und -Funktionen
im Part 2 des Standards [ISO/IEC 9075 2003].
Dieser Beitrag dient als »State of the Art« im Feld der analytischen Operationen und Funktionen. Wir beschreiben sowohl die
gesamte im SQL-Standard vorgeschlagene OLAP-Funktionalität
als auch das, was davon in den wichtigsten existierenden relationalen Datenbanksystemen implementiert wurde. Genauer gesagt,
wir vergleichen die im SQL:2003-Standard spezifizierten analytischen Operationen und Funktionen mit den entsprechenden Operationen und Funktionen, die die Hersteller IBM, Microsoft und
Oracle in ihren Datenbanksystemen implementiert haben. Dabei
wird u.a. gezeigt, welche Ähnlichkeiten und Unterschiede bei der
Implementierung von analytischen Operationen und Funktionen
zwischen den Datenbanksystemen DB2, Oracle und SQL Server
existieren.
Dieser Beitrag hat zwei Teile. Jeder Teil umfasst zwei Kapitel.
Nach der allgemeinen Einführung im ersten Kapitel, werden im
zweiten Kapitel alle im SQL-Standard vorgeschlagenen Erweiterungen in Bezug auf SQL/OLAP dargestellt. Zunächst wird das
Window-Konstrukt zusammen mit allen seinen Teilen erörtert.
Danach behandelt dieses Kapitel die Erweiterungen der GROUP
BY-Klausel, und zum Schluss wird die Menge von im Zusammenhang mit der Datenanalyse spezifizierten Funktionen diskutiert.
Die Implementierung von SQL/OLAP-Operationen und -Funktionen in Oracle, DB2 und SQL Server ist das Thema des vierten
Kapitels. In diesem Kapitel wird gezeigt, welche Funktionalität
von SQL/OLAP, die im SQL-Standard spezifiziert wurde, von
den Datenbankherstellern implementiert wurde. Kapitel 5 erläutert sowohl die proprietären OLAP-Erweiterungen der drei Datenbankhersteller als auch die Funktionalität, die vom SQL-Stan-
Unterstützung von SQL/OLAP
Fachthema
dard abweicht. Der Beitrag wird mit einer Zusammenfassung
(siehe Kapitel 3) und einer tabellarischen Darstellung aller standardisierten SQL/OLAP-Operationen und -Funktionen abgeschlossen (Kapitel 4 und 5 werden im Heft 23 erscheinen).
order clause«) aus einem existierenden Window-Konstrukt zu
übernehmen, sodass diese Teile nicht mehrfach geschrieben werden müssen. (Diese Vorgehensweise erleichtert es dem Optimierer, die Gleichheit von ORDER BY-Klauseln zu erkennen.)
2 SQL/OLAP im SQL-Standard
Weil die obige Spezifikation eines Window-Konstruktes manchmal kompliziert sein kann, unterstützt der SQL-Standard zwei
verschiedene Formen für die Definition von analytischen Funktionen:
Die OLAP-Funktionalität im SQL-Standard kann in drei Teile unterteilt werden:
• das Window-Konstrukt
• die Erweiterung der GROUP BY-Klausel
• Anfragefunktionen
Die folgenden Abschnitte beschreiben diese Teile.
2.1 Das Window-Konstrukt
Die wohl wichtigste Erweiterung, die SQL/OLAP enthält, ist das
Window-Konstrukt. Ein Fenster (Window) spezifiziert eine vom
Benutzer definierte Menge von Reihen, auf die eine Funktion angewendet wird. Die Größe eines Fensters kann dabei unterschiedlich spezifiziert werden: Meistens wird, ausgehend von der aktuellen Reihe, ein Reihenbereich definiert.
Die Syntax des Window-Konstruktes innerhalb der Tabellenspezifikation einer SELECT-Anweisung sieht folgendermaßen aus:
<table expression> ::=
<from clause>
[ <where clause> ]
[ <group by clause> ]
[ <having clause> ]
[ <window clause> ]
<window clause> ::=
WINDOW <window definition list>
<window definition list> ::=
<window definition> [ { <comma>
<window definition> } ... ]
<window definition> ::=
<new window name> AS <window specification>
<new window name> ::= <window name>
<window specification> ::=
<left paren> <window specification details>
<right paren>
<window specification details> ::=
[ <existing window name> ]
[ <window partition clause> ]
[ <window order clause> ]
[ <window frame clause> ]
<existing window name> ::= <window name>
Aus der obigen Syntax ist ersichtlich, dass die explizite Spezifikation eines Window-Konstruktes (»window specification«) immer am Ende aller in den SELECT-Anweisungen definierten
Klauseln angegeben werden muss. Genauso spezifiziert der SQLStandard, dass ein Window-Konstrukt neben »existing window
name« drei optionale Teile enthält:
• Partitionierung (»window partition clause«)
• Sortierung (»window order clause«)
• »window frame clause«
»existing window name« erlaubt es, die PARTITION BY-Klausel
(»window partition clause«) und ORDER BY-Klausel (»window
• die explizite Form
• die »in-line«-Form (»in-line window specification«)
Die folgende Syntax für analytische Funktionen (»window function«), die in Abschnitt 2.6 beschrieben werden, zeigt, wie die
»in-line«-Form verwendet werden kann:
<window function> ::=
<window function type> OVER
<window name or specification>
<window function type> ::=
<rank function type> <left paren>
<right paren>
| ROW_NUMBER <left paren> <right paren>
| <aggregate function>
<rank function type> ::=
RANK | DENSE_RANK | PERCENT_RANK | CUME_DIST
<window name or specification> ::=
<window name>
| <in-line window specification>
<in-line window specification> ::=
<window specification>
Die explizite Form des Window-Konstruktes wird, wie schon erwähnt, am Ende aller Klauseln der SELECT-Anweisung spezifiziert (siehe Beispiel 2). Die »in-line«-Form des Window-Konstruktes wird in der Projektion der entsprechenden Anfrage
formuliert (siehe Beispiel 3).
Bevor wir anfangen, das Window-Konstrukt und SQL/OLAPFunktionen zu zeigen, werden wir die Tabelle employee einführen, die in fast allen Beispielen verwendet wird.
Beispiel 1
Dieses Beispiel zeigt die Erstellung der Tabelle employee gemeinsam mit den INSERT-Anweisungen, die einige Reihen in diese
Tabelle einfügen.
CREATE TABLE employee
(emp_id INT NOT NULL,
dept_id INT NOT NULL,
first_name VARCHAR(25),
last_name VARCHAR(25),
salary DECIMAL(8,2),
job VARCHAR(20),
year INT);
INSERT INTO employee VALUES (1111, 10,
'Martha', 'White', 4400, 'IT_PROG',2001);
INSERT INTO employee VALUES (1112, 10,
'John', 'Black', 8800, 'IT_PROG', 2003);
INSERT INTO employee VALUES (1113, 20,
'Bill', 'Austin', 7600, 'MK_REP', 2001);
INSERT INTO employee VALUES (1114, 20,
'Diana', 'Kimes', 4300, 'MK_MAN', 2003);
Datenbank-Spektrum 22/2007
45
Fachthema
Unterstützung von SQL/OLAP
INSERT INTO employee VALUES (1115, 20,
'David', 'Peters', 7600, 'IT_PROG', 2004);
INSERT INTO employee VALUES (1116, 30,
'Sibille', 'Peterson',12000,'AX_ASST', 2001);
INSERT INTO employee VALUES (1117, 30,
'Jack', 'Klein', 9900, 'MK_REP', 2003);
INSERT INTO employee VALUES (1118, 30,
'Alex', 'Armstrong', 8500, 'MK_REP', 2004);
INSERT INTO employee VALUES (1119, 30,
'Jennifer', 'May', 6700, 'AX_ASST', 2005);
INSERT INTO employee VALUES (1120, 40,
'Roy', 'Hunt', 9900, 'IT_PROG', 2005);
INSERT INTO employee VALUES (1121, 40,
'Wendy', 'Blunt', 8800, 'AX_ASST', 2004);
INSERT INTO employee VALUES (1122, 50,
'Valli', 'Begg', 7900, 'MK_MAN', 2001);
INSERT INTO employee VALUES (1123, 50,
'Pat', 'Donaldson', 4900, 'MK_MAN', 2001);
Abbildung 1 zeigt den Inhalt der Tabelle employee.
2.2 Die Bildung von Partitionen
Die Window-Partitionierung teilt die durch die Anfrage ausgewählten Reihen in eine oder mehrere Partitionen. Sie hat sehr viele Ähnlichkeiten mit der Gruppierung von Reihen mittels der
GROUP BY-Klausel, weil die Ergebnismenge, genauso wie bei
GROUP BY, nach einem oder mehreren Merkmalen gruppiert
wird. Im Unterschied zu GROUP BY, wo jede Gruppe als eine
einzelne Reihe in der Ergebnismenge vorkommt, gehört bei der
Window-Partitionierung jede einzelne Reihe der Partition zu der
Ergebnismenge. Die beiden Anfragen im folgenden Beispiel zeigen den Unterschied zwischen der Window-Partitionierung und
der GROUP BY-Klausel.
Beispiel 2
SELECT dept_id, SUM(salary) AS summe
FROM employee
GROUP BY dept_id;
SELECT dept_id, last_name as name,
SUM(salary) OVER (PARTITION BY dept_id)
AS summe
FROM employee;
Die Ausgaben der beiden Anweisungen sehen folgendermaßen
aus:
dept_id
--------10
20
30
40
50
summe
-----------------------------13200.00
19500.00
37100.00
18700.00
12800.00
dept_id
--------10
10
20
20
20
30
30
30
30
40
40
50
50
name
----------------White
Black
Austin
Kimes
Peters
Peterson
Klein
Armstrong
May
Hunt
Blunt
Begg
Donaldson
summe
-----------13200.00
13200.00
19500.00
19500.00
19500.00
37100.00
37100.00
37100.00
37100.00
18700.00
18700.00
12800.00
12800.00
Die erste SELECT-Anweisung liefert insgesamt 5 Reihen (für
jeden dept_id-Wert je eine), während die zweite alle Reihen der
Tabelle employee ausgibt.
Ein weiterer Unterschied zwischen dem Window-Konstrukt und
der GROUP BY-Klausel ist ebenfalls im vorherigen Beispiel zu
erkennen: Im Falle des Window-Konstruktes darf die Projektion
beliebige Spalten der Tabelle enthalten. (Das ist die Konsequenz
daraus, dass die Ergebnismenge einzelne Reihen in jeder Gruppierung enthält.)
Beispiel 3
Dieses Beispiel zeigt die einfache Form einer Partitionierung mit
der expliziten Form des Window-Konstruktes.
SELECT dept_id, last_name AS name,
SUM(salary) OVER w AS sum_sal_dept
FROM employee
WINDOW w AS (PARTITION BY dept_id);
emp_id
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
46
dept_id
10
10
20
20
20
30
30
30
30
40
40
50
50
first_name
Martha
John
Bill
Diana
David
Sibille
Jack
Alex
Jennifer
Roy
Wendy
Valli
Pat
last_name
salary
White
4400.00
Black
8800.00
Austin
7600.00
Kimes
4300.00
Peters
7600.00
Peterson
12000.00
Klein
9900.00
Armstrong
8500.00
May
6700.00
Hunt
9900.00
Blunt
8800.00
Begg
7900.00
Donaldson 4900.00
Datenbank-Spektrum 22/2007
job
IT_PROG
IT_PROG
MK_REP
MK_MAN
IT_PROG
AX_ASST
MK_REP
MK_REP
AX_ASST
IT_PROG
AX_ASST
MK_MAN
MK_MAN
year
2001
2003
2001
2003
2004
2001
2003
2004
2005
2005
2004
2001
2001
Abb. 1: Inhalt der Tabelle employee
Unterstützung von SQL/OLAP
Beispiel 4
Dieses Beispiel entspricht dem vorherigen Beispiel, das WindowKonstrukt ist diesmal »in-line« in der Projektion der SELECTAnweisung definiert.
SELECT dept_id, last_name AS name,
SUM(salary) OVER (PARTITION BY
dept_id) AS sum_sal_dept
FROM employee;
Die PARTITION BY-Klausel in den Beispielen 2 und 3 spezifiziert, dass alle Reihen der Tabelle employee nach den Werten der
Spalte dept_id gruppiert (partitioniert) werden und danach die
Summe von Gehältern für jede Partition gebildet wird. Die
OVER-Klausel fasst die Reihen zusammen, auf die eine Funktion, in diesem Fall die Aggregatfunktion SUM, angewendet
wird. (Die Funktionen, die auf ein Window-Konstrukt angewendet werden können, werden analytische Funktionen genannt.)
Die standardisierte Syntax des Window-Konstruktes ermöglicht
auch die Definition eines »leeren« Fensters, weil alle drei Teile
der Klausel optional sind.
Beispiel 5
Dieses Beispiel zeigt die einfachste Form eines Fensters. Die Anfrage ermittelt alle Reihen der Tabelle employee mit der Aufsummierung aller Gehälter in der letzten Spalte der Ergebnismenge.
SELECT dept_id, last_name AS name,
SUM(salary) OVER () AS summe
FROM employee;
2.3 Sortierung innerhalb von Partitionen
Die zweite optionale Komponente des Window-Konstruktes ist
die Sortierung. Die Sortierung wird mithilfe der ORDER BYKlausel für jede einzelne Partition durchgeführt. Die Syntax für
diesen Teil des Window-Konstruktes sieht folgendermaßen aus:
<window order clause> ::=
ORDER BY <sort specification list>
<sort specification list> ::=
<sort specification> [
{ <comma> <sort specification> } ... ]
<sort specification> ::=
<sort key>
[ <ordering specification> ] [
<null ordering> ]
<sort key> ::= <value expression>
<ordering specification> ::= ASC | DESC
<null ordering> ::= NULLS FIRST | NULLS LAST
Wie aus der Syntax ersichtlich, entspricht die ORDER BY-Klausel der gleichnamigen Klausel in der SELECT-Anweisung. Der
einzige Unterschied ist die Existenz der NULLS FIRST- bzw.
NULLS LAST-Angabe. Diese Angaben spezifizieren, wie die
NULL-Werte in jeder Partition geordnet werden sollen. Im Falle
von NULLS FIRST werden alle NULL-Werte in der entsprechenden Sortierung vor bzw. bei NULLS LAST nach allen anderen
Werten innerhalb der jeweiligen Partition sortiert. (Es können
mehrere verschiedene Sortierungen innerhalb eines WindowKonstruktes existieren.)
Die Sortierung der Reihen für die ORDER BY-Klausel der
SELECT-Anweisung ist nach der Spezifikation des Standards
Fachthema
»implementation defined«, das bedeutet, dass Datenbankhersteller selbst entscheiden können, ob sie NULL-Werte vor oder hinter
allen Nicht-NULL-Werten platzieren möchten.
Beispiel 6
In diesem Beispiel wird innerhalb eines »in-line«-Fensters die
Sortierung nach dem Familiennamen des Mitarbeiters durchgeführt. Weil keine PARTITION BY-Klausel angegeben ist, wird
die ganze Ergebnismenge als eine Partition betrachtet.
SELECT dept_id, last_name AS name,
SUM(salary) OVER(ORDER BY last_name NULLS
FIRST ROWS UNBOUNDED PRECEDING) AS sum_sal
FROM employee;
2.4 »Window frame clause«
Die letzte Komponente – »window frame clause« – spezifiziert,
auf welche Reihen aus der Umgebung der aktuellen Reihe eine
Aggregatfunktion angewendet wird. Die Syntax sieht folgendermaßen aus:
<window frame clause> ::=
<window frame units>
<window frame extent>
[ <window frame exclusion> ]
<window frame units> ::= ROWS | RANGE
<window frame extent> ::=
<window frame start>
| <window frame between>
<window frame start> ::= UNBOUNDED PRECEDING
| <unsigned value specification> PRECEDING
| CURRENT ROW
<window frame between> ::= BETWEEN
<window frame bound 1> AND
<window frame bound 2>
<window frame bound 1> ::= <window frame bound>
<window frame bound 2> ::= <window frame bound>
<window frame bound> ::=
<window frame start>
| UNBOUNDED FOLLOWING
| <unsigned value specification> FOLLOWING
<window frame exclusion> ::=
EXCLUDE CURRENT ROW
| EXCLUDE GROUP
| EXCLUDE TIES
| EXCLUDE NO OTHERS
Wie aus der Syntax ersichtlich, existieren für »window frame
clause« drei Angaben. Die erste Angabe – »window frame units«
– bezieht sich auf die möglichen Arten der Reihengruppierung:
ROWS bzw. RANGE. Die zweite Angabe – »window frame extent« – spezifiziert die Endpunkte des Fensters, das ausgegeben
werden soll. Schließlich gibt es auch die Möglichkeit, die Endpunkte des Fensters durch die Negation, d.h. durch das Ausschließen von explizit genannten Reihen, festzulegen (»window frame
exclusion«).
Um die Begriffe ROWS und RANGE zu erläutern, werden wir
zuerst die Nachbarschaft der aktuellen Reihe (CURRENT ROW)
definieren. Die Nachbarschaft einer aktuellen Reihe ist eine Teilmenge der Partition, zu der diese gehört, und zwar ein Reihenintervall bezüglich der angegebenen Sortierung.
Datenbank-Spektrum 22/2007
47
Fachthema
Unterstützung von SQL/OLAP
Bei der Angabe ROWS werden die Untergrenze und Obergrenze
dieses Intervalls durch die relative Position zur aktuellen Reihe
angegeben, also die Anzahl von Reihen vor oder nach der aktuellen Reihe, an der die jeweilige Grenze liegt.
Bei der Angabe RANGE werden die Untergrenze und Obergrenze dieses Intervalls durch den Vergleich mit den Sortierkriterien
bestimmt; das Intervall enthält also mit jeder Reihe auch alle Duplikate (bei ROWS ist das nicht der Fall). Wenn in der ORDER
BY-Klausel genau ein Sortierkriterium steht, kann man auch den
Abstand der Grenze zur aktuellen Reihe angeben, nämlich durch
die Differenz des Sortierkriteriums zwischen der aktuellen Reihe
und der jeweiligen Grenze. Dabei muss in der ORDER BY-Klausel genau ein Sortierkriterium stehen, und sein Datentyp muss die
Bildung von Differenzen zulassen: Es muss also einen numerischen Typ, einen DATETIME-Typ oder einen INTERVAL-Typ
enthalten.
In beiden Fällen (ROWS und RANGE) kann die Nachbarschaft
einer aktuellen Reihe leer sein: Bei ROWS ist z.B. eine Nachbarschaft, die aus den zehn direkten Vorgängern besteht (ROWS
BETWEEN 10 PRECEDING AND 1 PRECEDING), immer
dann leer, wenn die CURRENT ROW die erste Reihe ihrer Partition ist; und wenn wir mit RANGE als Nachbarschaft die Reihen
aus den zwei vorherigen Jahren suchen (RANGE BETWEEN 2
PRECEDING AND 1 PRECEDING), so ist es durchaus möglich,
dass es keine Reihen für die gesuchten Jahre gibt.
Beispiel 7
Das Beispiel zeigt die Verwendung der RANGE-Angabe innerhalb einer »window frame clause«.
SELECT year, last_name AS name,
COUNT(salary) OVER(ORDER BY dept_id
RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS cnt
FROM employee
WHERE year = 2001;
Beispiel 8
Das Beispiel zeigt, wie man mithilfe der ROWS-Angabe innerhalb einer »window frame clause« sogenannte »gleitende Aggregate« (sliding aggregations) implementieren kann.
SELECT year, last_name AS name,
COUNT(salary) OVER(ORDER BY dept_id
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS cnt
FROM employee
WHERE year = 2001;
Die Ergebnisse der beiden vorherigen Beispiele zeigen den Unterschied für die beiden Angaben RANGE und ROWS:
YEAR
NAME
2001
2001
2001
2001
2001
-----------
White
Austin
Peterson
Begg
Donaldson
-----------
48
Datenbank-Spektrum 22/2007
CNT
1
1
1
2
2
-----
2001
2001
2001
2001
2001
White
Austin
Peterson
Begg
Donaldson
2
3
3
3
2
Mit der BETWEEN-Angabe werden die Untergrenze und Obergrenze von »window frame« in Bezug auf die aktuelle Reihe festgelegt, z.B.:
BETWEEN UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING
Ohne diese Angabe ist die Obergrenze immer CURRENT ROW
und nur die Untergrenze wird explizit festgelegt, z.B.:
ROWS 3 PRECEDING
Aus o.g. Grund sind folgende Angaben identisch:
...ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
...ROWS 3 PRECEDING
Der SQL-Standard erlaubt nur bei der BETWEEN-Angabe die
obere Grenze des Fensters explizit zu spezifizieren. Des Weiteren
ist bei der BETWEEN-Angabe die Reihenfolge der Grenzen festgelegt und kann nicht umgekehrt spezifiziert werden.
Mit EXCLUDE kann man aus einem »window frame« diejenigen
Reihen entfernen, die in allen Sortierkriterien mit der aktuellen
Reihe übereinstimmen, also:
• die aktuelle Reihe selber (EXCLUDE CURRENT ROW)
• nur die Reihen mit denselben Werten für die Sortierkriterien
wie die aktuelle Reihe (EXCLUDE TIES)
• sowohl die aktuelle Reihe als auch TIES (EXCLUDE
GROUP)
2.5 Erweiterung der GROUP BY-Klausel
Der SQL-Standard erweitert die GROUP BY-Klausel um drei Angaben:
• ROLLUP
• CUBE
• GROUPING SETS
Beispiel 9
In diesem Beispiel wird gezeigt, welcher Unterschied zwischen
der Gruppierung mit der GROUP BY-Klausel und der Erweiterung von GROUP BY durch die ROLLUP-Angabe existiert.
SELECT dept_id, year, SUM(salary) AS sum1
FROM employee
GROUP BY(dept_id, year);
SELECT dept_id, year, SUM(salary) AS sum1
FROM employee
GROUP BY ROLLUP(dept_id, year);
Der Hauptunterschied zwischen der ersten und der zweiten
SELECT-Anweisung ist, dass die GROUP BY-Klausel in der ersten nur Werte von einzelnen Gruppierungen nach den entsprechenden Spalten ausgibt, während die zweite durch die ROLLUPAngabe zusätzliche Werte, die sogenannten Super-Aggregate, er-
Fachthema
Unterstützung von SQL/OLAP
mittelt und ausgibt. (Super-Aggregate sind Summen, die durch
Aufaddierung nach einem Gruppierungsmerkmal entstehen.
Summe aller Gehälter für dept_id=10 in der Tabelle employee
wäre ein Beispiel für ein Super-Aggregat.)
alternativ ersetzt werden. Damit kann eine ROLLUP-Gruppierung durch eine Folge von Gruppenmengen und mithilfe der
GROUPING SETS-Klausel folgendermaßen implementiert werden:
Die folgenden Reihen sind der Teil der Ergebnismenge der zweiten SELECT-Anweisung, die in der ersten nicht vorkommen:
GROUP BY ROLLUP (dept_id, year) ist äquivalent zu:
DEPT_ID
10
20
30
40
50
null
YEAR
null
null
null
null
null
null
SUM1
13200
19500
37100
18700
12800
101300
Wie aus dem Teilergebnis zu sehen ist, wird die Aufaddierung
nach einer Gruppierungsspalte durch »null« angezeigt. Die Aufaddierung der Werte beider Gruppierungsspalten wird dementsprechend durch zwei »null«-Angaben spezifiziert.
Die ROLLUP-Angabe erstellt die Super-Aggregate nur über die
letzte angegebene Gruppierungsspalte, über die beiden letzten
und schließlich über alle angegebenen Gruppierungsspalten (das
sogenannte »grand total«). Aus diesem Grund ist die Ausgabe für
die folgenden beiden Angaben unterschiedlich:
GROUP BY ROLLUP(dept_id, year);
GROUP BY ROLLUP(year, dept_id);
Falls die Ausgabe aller möglichen Kombinationen der Super-Aggregate für beide Spalten erwünscht ist, wird die CUBE-Angabe
verwendet.
Beispiel 10
Dieses Beispiel gibt mithilfe der CUBE-Klausel alle möglichen
Kombinationen für beide Spalten dept_id und year aus.
SELECT dept_id, year, SUM(salary)
FROM employee
GROUP BY CUBE(dept_id, year);
Während die CUBE- und ROLLUP-Klauseln eine vordefinierte
Menge von Super-Aggregaten für die Ergebnismenge einer Anfrage ermitteln, berechnet die GROUPING SETS-Klausel Gruppen in Bezug auf verschiedene Spaltengruppen in derselben Anfrage. Die Verwendung dieser Klausel ist komplexer als die
Verwendung der anderen beiden, erlaubt aber eine feinere Auswahl von Gruppen.
Beispiel 11
Dieses Beispiel zeigt die Gruppierung nach zwei verschiedenen
Gruppen: dept_id und der Kombination der beiden Spalten
dept_id und year.
SELECT dept_id, year, SUM(salary) AS summe
FROM employee
GROUP BY GROUPING SETS ((dept_id),
(dept_id, year));
Die Erstellung von Super-Aggregaten mit den CUBE- und
ROLLUP-Klauseln kann durch eine Reihe von Gruppenmengen
GROUP BY GROUPING SETS ((dept_id, year),
(dept_id), ())
Genauso kann eine Gruppierung mithilfe der CUBE-Klausel
durch alle Permutationen der Elemente der Gruppenmengen ersetzt werden:
GROUP BY CUBE (dept_id, year) ist äquivalent zu:
GROUP BY GROUPING SETS ((dept_id, year),
(dept_id), (year), ())
Eine zusätzliche Erweiterung der GROUP BY-Klausel ist die
»feature« T434 des SQL-Standards, die die Angaben ALL (der
Standardwert) und DISTINCT zu dieser Klausel erlaubt. Mit der
Angabe DISTINCT werden Duplikate aus der Spezifikation für
GROUPING SETS eliminiert. (Kein einziges Datenbanksystem
unterstützt diese Erweiterung.)
2.6 SQL/OLAP-Anfragefunktionen
Die SQL/OLAP-Anfragefunktionen können in folgende Gruppen
unterteilt werden:
•
•
•
•
•
•
•
Aggregatfunktionen
Rangfunktionen
die GROUPING-Funktion
statistische Funktionen
inverse Verteilungsfunktionen
hypothetische Aggregatfunktionen
die WIDTH_BUCKET-Funktion
Die Aggregatfunktionen SUM, AVG, COUNT, MAX und MIN
werden in diesem Beitrag nicht erörtert, weil sie schon länger im
SQL-Standard spezifiziert und von etlichen Datenbanksystemen
implementiert sind.
Die oben genannten Funktionen lassen sich in zwei Gruppen unterteilen: erstens in diejenigen, die als »normale« Funktionen verwendet werden können, und zweitens in diejenigen, die zusätzlich
als analytische Funktionen (»window functions«) benutzt werden
können.
Von allen oben genannten Funktionen können hypothetische Aggregatfunktionen und die Funktion WIDTH_BUCKET nicht als
analytische Funktionen verwendet werden.
2.6.1 Rangfunktionen
Rangfunktionen geben für jede Reihe einer Partition einen Rang
als Nummer aus. Folgende Rangfunktionen werden vom SQLStandard unterstützt:
• RANK
• DENSE_RANK
Folgende Funktionen werden als Verteilungsfunktionen
(»distributed functions«) im SQL-Standard genannt:
• PERCENT_RANK
• CUME_DIST
Datenbank-Spektrum 22/2007
49
Fachthema
Unterstützung von SQL/OLAP
Um die Terminologie zu vereinfachen, werden wir im weiteren
Text alle vier o.g. Funktionen als Rangfunktionen bezeichnen.
Die ersten beiden Funktionen geben eine natürliche Zahl als Ergebnis zurück, während die letzten beiden einen normalisierten
Wert (einen Wert zwischen 0 und 1) liefern. (Eine weitere Funktion, die auch hier beschrieben wird, obwohl sie nicht zu den
Rangfunktionen gehört, ist die ROW_NUMBER-Funktion.)
Beispiel 12
In diesem Beispiel werden die drei Funktionen RANK,
DENSE_RANK und ROW_NUMBER gezeigt.
SELECT RANK() OVER (ORDER BY year) AS rnk,
DENSE_RANK() OVER (ORDER BY year)
AS dense_rnk,
ROW_NUMBER() OVER (ORDER BY year)
AS row_no, year
FROM employee;
Mithilfe der ROW_NUMBER-Funktion wird jeder Reihe innerhalb einer Partition ein eindeutiger Wert zugewiesen, wobei die
erste Reihe der Partition in der Sortierreihenfolge des Fensters
den Wert 1 liefert, für die zweite den Wert 2 usw. (Weil in unserem
Beispiel keine Partitionen spezifiziert sind, wird die ganze Ergebnismenge als eine Partition betrachtet.)
Die RANK-Funktion liefert für die aktuelle Reihe den Wert 1 plus
die Anzahl der Reihen in derselben Partition mit echt kleineren
Sortierkriterien als die betrachtete. Alle Reihen in einer Partition
mit gleichwertigen Sortierkriterien haben also denselben Rang,
und die Werte von RANK in einer Partition können Lücken aufweisen.
Die DENSE_RANK-Funktion liefert für die aktuelle Reihe den
Wert 1 plus die Anzahl der echt kleineren Sortierkriterien als die
betrachtete Reihe in der Partition. Alle Reihen in einer Partition
mit gleichwertigen Sortierkriterien haben also denselben
DENSE_RANK; die Werte von DENSE_RANK enthalten aber
keine Lücken. (RANK und DENSE_RANK liefern dieselben
Werte, falls es bezüglich der Sortierkriterien keine Duplikate
gibt.)
Mithilfe der ersten 6 Reihen der Ergebnismenge wird der Unterschied zwischen den Funktionen RANK und DENSE_RANK erörtert:
RNK
ROW_NO
YEAR
1
DENSE_RNK
1
1
2001
1
1
2
2001
1
1
3
2001
1
1
4
2001
1
1
5
2001
6
2
6
2003
Die PERCENT_RANK-Funktion gibt den Bruchteil der Reihen
mit echt kleineren Sortierkriterien als die aktuelle Reihe an, den
diese an allen Reihen der Partition außer der aktuellen Reihe haben. Dementsprechend wird die PERCENT_RANK-Funktion
durch die Formel: (RANK -1)/(n-1) berechnet, ist also ein normalisierter Wert (n stellt die Anzahl der Reihen in der Partition
50
Datenbank-Spektrum 22/2007
dar). Für n = 1 liefert PERCENT_RANK() den Wert 0e0. Der
Rückgabewert ist vom Datentyp »approximate numeric«, d.h.
FLOAT, DOUBLE PRECISION oder REAL.
CUME_DIST ist die Verteilungsfunktion von RANK: Sie ermittelt den relativen Rang einer Reihe innerhalb einer Partition, indem die relative Anzahl von Reihen bestimmt wird, deren Rang
kleiner gleich dem Rang der aktuellen Reihe ist. CUME_DIST
wird durch folgende Formel berechnet:
CUM_DIST(x) = (1/n)*Anzahl der Reihen <=x,
wobei n die Anzahl der Reihen in der Partition ist. Der Rückgabewert ist vom Datentyp »approximate numeric«.
2.6.2 Die GROUPING-Funktion
Bei der Erstellung eines Super-Aggregates wird der Wert der
Spalte, für die aufsummiert wird, durch »null« (den NULL-Wert)
gekennzeichnet. Damit der tatsächliche NULL-Wert von dem Super-Aggregat unterschieden werden kann, hat der SQL-Standard
die GROUPING-Funktion eingeführt.
Diese Funktion hat einen oder mehrere Spaltennamen als Parameter, die alle Gruppierungsspalten sein müssen. Ihr Wert ist ganzzahlig und positiv. In einer Reihe gibt er an, ob die Reihe zu einer
normalen Gruppe gehört (Wert = 0) oder zu einer Aggregierung
von Gruppen (Super-Aggregat) nach einer oder mehreren der angegebenen Gruppierungsspalten (Wert > 0).
Aus dem Wert der GROUPING-Funktion in Binärdarstellung
kann man die für die Super-Aggregate verwendeten Gruppierungsspalten erkennen: So liefert GROUPING(A, B, C) den Wert
1, wenn das Super-Aggregat nach der Spalte C erfolgt (aber nicht
nach A oder B), und den Wert 5, wenn das Super-Aggregat nach
den Spalten A und C erfolgt (aber nicht nach B).
Beispiele für die GROUPING-Funktion werden im gleichnamigen Abschnitt in Kapitel 4 (im Heft 23) gezeigt.
2.6.3 Statistische Funktionen
Zu den statistischen Funktionen gehören:
•
•
•
•
•
STDDEV_POP und STDDEV_SAMP
VAR_POP und VAR_SAMP
COVAR_POP und COVAR_SAMP
CORR
diverse Regressionsfunktionen
Die ersten zwei Funktionspaare enthalten einen Parameter, während der Rest der aufgelisteten Funktionen zwei Parameter hat.
Die Funktion STDDEV_POP(ausdruck) berechnet die Populations-Standardabweichung aller ausdruck-Werte für jede
Reihe der Partition. Genauso berechnet die Funktion
STDDEV_SAMP(ausdruck) die Stichproben-Standardabweichung aller ausdruck-Werte für jede Reihe der Partition.
Die Funktion VAR_POP(ausdruck) ermittelt die Varianz der
Population aller ausdruck-Werte für jede Reihe der Partition und
die Funktion VAR_SAMP(ausdruck) die Varianz der entsprechenden Stichprobe. Die Funktionen COVAR_POP und
COVAR_SAMP berechnen die Populations- bzw. StichprobenKovarianz für jede Reihe der Partition.
Unterstützung von SQL/OLAP
Die Funktion CORR(dependent_var, independent_var) berechnet
den Korrelationskoeffizienten zwischen einer Menge von abhängigen (dependent_var) und unabhängigen (independent_var) Variablen.
Im SQL-Standard wurden insgesamt 9 Regressionsfunktionen
spezifiziert. Ihre Namen und die kurze Beschreibung sind in Abbildung 2 [Kline et al. 2005] angegeben.
2.6.4 Inverse Verteilungsfunktionen
Die inversen Verteilungsfunktionen enthalten einen numerischen
Parameter, der dem Intervall [0;1] angehört. Der Parameterwert
stellt den prozentuellen Rang jeder Reihe innerhalb von Reihen,
die zu der entsprechenden Partition gehören, dar. Die Syntax der
inversen Verteilungsfunktionen sieht folgendermaßen aus:
<inverse distribution function> ::=
<inverse distribution function type>
<left paren>
<inverse distribution function argument>
<right paren>
<within group specification>
<inverse distribution function argument> ::=
<numeric value expression>
<inverse distribution function type> ::=
PERCENTILE_CONT | PERCENTILE_DISC
Fachthema
2.6.5 Hypothetische Aggregatfunktionen
Hypothetische Aggregatfunktionen stehen im Zusammenhang mit den schon erwähnten Rangfunktionen RANK,
DENSE_RANK, PERCENT_RANK und CUME_DIST. Die
Aufgabe von hypothetischen Aggregatfunktionen besteht darin,
den Rangwert zurückzugeben, der dem Parameterwert in der betreffenden Partition entsprechen würde, falls der angegebene
Wert für die Tabellenspalte tatsächlich existieren würde. Die Syntax sieht folgendermaßen aus:
<hypothetical set function> ::=
<rank function name> <left paren>
<hypothetical set function value
expression list> <right paren>
<within group specification>
<within group specification> ::=
WITHIN GROUP <left paren> ORDER BY
<sort specification list> <right paren>
<hypothetical set function value expression list>
::= <value expression [
{ <comma> <value expression> } ...]
Die Syntax für die hypothetischen Aggregatfunktionen unterscheidet sich von der Syntax der »reinen« Rangfunktionen. Die
Angabe WITHIN GROUP muss zusammen mit einer Rangfunktion spezifiziert werden, damit diese als hypothetische Funktion
erkannt wird. Beispiele für hypothetische Aggregatfunktionen
werden wir in Kapitel 4 geben.
Zu den inversen Verteilungsfunktionen gehören damit:
• PERCENTILE_DISC (perzentil)
• PERCENTILE_CONT (perzentil)
Die Funktion PERCENTILE_CONT generiert einen interpolierten Wert, der, wenn man ihn zu der Partition hinzufügen würde, dem angegebenen Parameter entspräche. Die Funktion
PERCENTILE_DISC liefert den Wert mit dem kleinsten kumulativen Verteilungswert, der größer/gleich dem angegebenen Parameter ist.
Beispiele für die beiden Verteilungsfunktionen werden in Kapitel 4 gegeben.
Hypothetische Aggregatfunktionen können nicht als analytische
Funktionen benutzt werden.
2.6.6 Die WIDTH_BUCKET-Funktion
Die WIDTH_BUCKET-Funktion teilt gegebene Werte auf Gruppen (»buckets«) gleicher Intervallgröße auf. Die Syntax dieser
Funktion sieht folgendermaßen aus:
WIDTH_BUCKET(ausdruck, min, max, buckets)
ausdruck ist ein Wert, der einer Gruppe zugewiesen wird. Er basiert auf einer oder mehreren von einer Abfrage zurückgegebenen
Spalten. buckets gibt an, wie viele Gruppen über den Wertebereich von min bis max definiert werden. Wenn sich ausdruck
Abb. 2: Vom SQL-Standard unterstützte Regressionsfunktionen
REGR_AVGX(dependent_var, independent_var)
REGR_AVGY(dependent_var, independent_var)
REGR_COUNT(dependent_var, independent_var)
REGR_INTERCEPT(dependent_var, independent_var)
REGR_R2(dependent_var, independent_var)
REGR_SLOPE(dependent_var, independent_var)
REGR_SXX(dependent_var, independent_var)
REGR_SXY(dependent_var, independent_var)
REGR_SYY(dependent_var, independent_var)
Berechnet den Durchschnittswert der unabhängigen
Variablen (independent_var).
Berechnet den Durchschnittswert der abhängigen Variablen
(dependent_var ).
Berechnet die Anzahl von Reihen, die in einer Partition übrig
geblieben sind.
Berechnet den y-Abschnitt der linearen Gleichung, die durch
das Paar (dependent_var, independent_var) festgelegt ist,
mithilfe der Methode des kleinsten Quadrats.
Berechnet das Quadrat des Korrelationskoeffizienten.
Berechnet die Steigung der linearen Gleichung, die durch das
Paar (dependent_var, independent_var) festgelegt ist, mithilfe
der Methode des kleinsten Quadrats.
Addiert die Quadrate der unabhängigen Variablen.
Addiert die Produkte aller Variablenpaare.
Addiert die Quadrate der abhängigen Variablen.
Datenbank-Spektrum 22/2007
51
Fachthema
Unterstützung von SQL/OLAP
nicht in dem Wertebereich der spezifizierten Gruppen befindet,
gibt die Funktion entweder 0 oder max+1 zurück, abhängig davon, ob ausdruck kleiner als min oder größer/gleich max ist.
Das Beispiel mit dieser Funktion wird in Kapitel 3 gezeigt. (Die
Funktion WIDTH_BUCKET kann nicht als analytische Funktion
verwendet werden.)
3 Kurze Zusammenfassung des ersten Teils
Der erste Teil des Beitrags gibt den Überblick über die im SQLStandard vorgeschlagenen Operationen und Funktionen für die
komplexe Analyse. Welche von diesen Operationen und Funktionen in großen Datenbanksystemen implementiert sind, wird im
zweiten Teil des Beitrags gezeigt, der im Heft 23 erscheinen wird.
Danksagung
An dieser Stelle möchte ich mich bei meinen Kollegen aus dem
SQL-Normierungsgremium, den Herren Michael Deckers und
Jost Enderle, für die konstruktive Kritik des Beitrags bedanken.
Literatur
[Celko 2006] Celko, J.: Analytics and OLAP in SQL. Morgan Kaufmann
Publishing, 2006.
[Gennick 2005] Gennick, J.: Find Answers Faster. Oracle Magazine, March/
April 2005.
[IBM DB2 a] IBM: DB2 Version 9 for Linux, UNIX and Windows, SQL
Reference Volume 1, 2006, ftp://ftp.software.ibm.com/ps/products/db2/
infro/vr9/pdf/letter/en_US/db2s1e90.pdf.
[IBM DB2 b] IBM: DB2 Version 9 for Linux, UNIX and Windows, SQL
Reference Vol. 2, 2006, ftp://ftp.software.ibm.com/ps/products/db2/infro/
vr9/pdf/letter/en_US/db2s2e90.pdf.
[ISO/IEC 13294 2001] ISO/IEC 13249-5-2001 Information technology –
Database languages – SQL – Part 5: Host Language Bindings –
AMENDMENT 1: On-Line Analytical Processing (SQL/OLAP).
[ISO/IEC 9075 2003] ISO/IEC 9075-2:2003 Information technology – Database languages – SQL – Part 2: Foundation (SQL/Foundation).
[Kline et al. 2005] Kline, K.; Kline, D.; Hunt, B.: XML in a Nutshell.
O’Reilly, 2005.
[Lehner 2003] Lehner, W.: Datenbanktechnologie für Data-WarehouseSysteme. dpunkt.verlag, Heidelberg, 2003.
[Melton 2003] Melton, J.: Advanced SQL:1999. Morgan Kaufmann
Publishing, 2003.
[Melton & Simon 2002] Melton, J.; Simon, A.: SQL:1999: Understanding
Relational Language Components. Morgan Kaufmann Publishing, 2002.
[Oracle 2007] Oracle-Dokumentation, 2007, www.oracle.com/technology/
docmentation/index.html.
[Petkovi… 2006a] Petkovi…, D.: Open Windows of Opportunity. SQL Server
Magazine, June 2006.
[Petkovi… 2006b] Petkovi…, D.: SQL Server 2005. dpunkt.verlag,
Heidelberg, 2006.
[Türker & Gertz 2000] Türker, C.; Gertz, M.: Semantic Integrity Support in
SQL-99 and Commercial (Object-)Relational Database Management
Systems. VLDB Journal, Vol. 10, No. 4, 2000.
[Winter 2000] Winter, R.: SQL-99’s New OLAP Functions. Intelligent Enterprise, Vol. 3, January 2000.
[Zemke et al. 1999] Zemke, F.; Kulkarni, K.; Witkowsky, A.; Lyle, B.:
Introduction to OLAP Functions. ISO/IEC JTC1/ SC32 WG3:YGJ-068
ANSI NCITS H2-99-154r2, May 1999.
52
Datenbank-Spektrum 22/2007
Dušan Petković
ist Professor für Datenbanksysteme an der FH
Rosenheim. Seine Arbeitsschwerpunkte liegen in
den Bereichen Performance Tuning und Datenbankmodellierung. Er ist Mitglied des deutschen
Normierungsgremiums für die SQL-Sprache.
Prof. Dr. Dušan Petković
Fachhochschule Rosenheim
Fakultät für Informatik
Hochschulstr. 1
83024 Rosenheim
[email protected]
www.fh-rosenheim.de
Herunterladen