===== 1.7.2) DQL - Data Query Language =====
===== 1.7.2.1) Grundkonstruktion einer SQL-Abfrage =====
Grundkonstruktion einer SQL-Abfrage ist von der Form
\\
\\
"**SELECT** //attributListe// **FROM** //relation//"
== Beispiel ==
SELECT name,adresse FROM restaurant;
entspricht \\
$ π_{name,adresse}(restaurant) $
\\
\\
SELECT * FROM restaurant;
/*liefert die gesamte Tabelle (d.h. mit allen Attributen)
\\
\\
**SQL-Abfrage mit Verknüpfung mehrerer Relationen:**
\\
\\
"**SELECT** //attributListe// **FROM** //relationen// **WHERE** //bedingung//"
\\
\\
Eine solche SQL-Abfrage wird ausgewertet, indem zunächst das **kartesische Produkt** der in der **FROM**-Klausel angegebenen Relationen gebildet wird. Daraus werden dann jene Tupel ausgewählt, die die in der **WHERE**-Klausel angegebenen Bedingungen erfüllen. Zudem wird auf die in der **SELECT**-Klausel angegebenen Attribute dieser Tupel projiziert.
== Beispiel ==
Wir wollen wissen welche Speisen in welchem Restaurant angeboten werden.
SELECT * FROM Speise, Restaurant WHERE Restaurant.rnr = Speise.rnr;
Die Bedingunng in der WHERE-Klausel kann sich aus mehreren, durch die logischen Operatoren AND, OR und NOT verbundenen Teilbedingungen zusammensetzen.
== Beispiel ==
SELECT rnr, name, haube, typ
FROM restaurant
WHERE haube > 1 AND NOT (typ='österreichisch' OR typ='international');
In der SELECT-Klausel können auch Ausdrücke, die aus Attributen berechnet werden, stehen. Durch das Schlüsselwort **AS** kann einem so berechneten Ausdruck ein Attributname zugewiesen werden.
==Beispiel==
Wir wollen alle Paare von Restaurants auswählen, die sich um mehr als zwei Hauben unterscheiden und die Differenz der Hauben ausgeben.
SELECT r1.name, r2.name, (r2.haube-r1.haube) AS differenz
FROM restaurant r1, restaurant r2
WHERE r1.haube +2 < r2.haube;
In SQL werden Duplikate, d.h. Ergebnistupel mit identischen Werten, nicht automatisch eliminiert. Um Duplikate zu eliminieren, muss der Liste von ausgewählten Attribute das Schlüsselwort **DISTINCT** vorangestellt werden.\\
Durch die Angabe einer ORDER BY-Klausel mit den Schlüsselworten ASC oder DESC kann die Ausgabe aufsteigend bzw. absteigend nach bestimmten Attributen sortiert werden. Der Defaultwert ist ASC.
== Beispiel ==
Wir wollen eine alphabetisch sortierte Liste aller Speisen von Restaurants mit mehr als einer Haube.
SELECT DISTINCT s.name
FROM Restaurant r, Speise r
WHERE (r.rnr = s.rnr) AND (r.haube>1)
ORDER BY s.name, s.preis;
==== Übungsbeispiele ====
Gegeben sind folgende Relationen
\\
\\
**Person** (__id__, name, plz) \\
**Orte** (__plz__, ort) \\
**Mitarbeiter** (__id__, gehalt, abtnr) \\
**Kunde** (__id__, rabatt)\\
**Abteilung** (__abtnr__, name)\\
**PKW** (marke, ps, id, __fahrgestellnr__)\\
== Aufgaben ==
Entwickeln Sie SQL-Anweisungen für folgende Abfragen
- Geben Sie die Namen aller Personen aus Amstetten aus
- Geben Sie die Namen aller Mitarbeiter aus Amstetten aus
- Gesucht sind die Namen aller Mitarbeiter die auch Kunden sind
- Namen aller Mitarbeiter die PKWs mit mehr als 1000 PS fahren
- Gesucht sind die Namen jener Orte, in denen Mitarbeiter wohnen, die in einer Abteilung "Buchhaltung" arbeiten
== Lösungen ==
1)
SELECT name
FROM Person p, Orte o
WHERE o.ort="Amstetten" and p.plz=o=plz;
2)
SELECT name
FROM Person p, Orte o, Mitarbeiter m
WHERE m.id=p.id AND o.ort="Amstetten" AND p.plz=o.plz;
3)
SELECT name
FROM Person p, Orte o, Mitarbeiter m
WHERE m.id=p.id AND m.id=k.id;
4)
SELECT name
FROM Person p, Mitarbeiter m, PKW
WHERE p.id=m.id AND PKW.id=p.id AND PKW.ps>100;
5)
SELECT ort
FROM Orte o, Mitarbeiter m, Person p, Abteilung a
WHERE o.plz=p.plz AND p.id=m.id AND m.abtnr=a.abtnr AND p.plz=o.plz;
===== 1.7.2.2) Die WHERE-Klausel =====
Neben den Boolschen Funktionen (AND,OR,NOT) sind folgende weitere Angaben zulässig:
==== IN-Operator ====
Anstatt eine Reihe von OR-Verknüpfungen durchzuführen, kann auch der IN-Operator verwendet werden.
Bsp:
SELECT * FROM ... WHERE typ IN ('österreichisch', 'international');
==== BETWEEN-Operator ====
Bsp.:
SELECT * FROM ... WHERE preis BETWEEN 100 and 150;
//ist dasselbe wie
SELECT * FROM ... WHERE preis >= 100 and preis<=150;
==== IS NULL-Operator ====
Bsp.: Es sollen alle Personen gesucht werden, die beim Attribut TelNr den NULL-Wert eingetragen haben.
SELECT * FROM ... WHERE TelNr IS NULL;
==== LIKE-Operator ====
Der Like-Operator ist ein Vergleichsoperator, der Datenwerte einer Spalte mit einem vorgegebenen Muster vergleicht.
\\
\\
Allgemeine Form:
\\
\\
AttributName [NOT] LIKE 'muster';
\\
\\
Dabei haben in 'muster' zwei Zeichen eine besondere Bedeutung: \\
* % .... steht für 0 bis beliebig viele Zeichen
* _ .... steht für genau ein beliebiges Zeichen
\\
\\
Bsp.: Alle Speisen deren Name mit 'M' beginnt
SELECT Name FROM Speise WHERE Name LIKE 'M%';
\\
Bsp.: Alle Restaurants, welche an 2. Stelle im Namen ein 'o' haben
SELECT Name FROM Restaurant WHERE Name LIKE '_o%';
===== 1.7.2.3) Mengenoperationen =====
SQL stellt die Mengenoperationen **UNION** (Vereinigung), **INTERSECT** (Durchschnitt) und **EXCEPT** (Mengendifferenz) für typkompatible Relationen (d.h. Relationen mit der gleichen Anzahl von jeweils typkompatiblen Attributen) zur Verfügung.
\\
\\
**Beispiel:** Wir wollen eine Liste der Namen aller Restaurants und der Namen aller Speisen
(SELECT Name FROM Restaurant)
UNION
(SELECT Name FROM Speise)
oder
SELECT Name FROM Restaurant WHERE Name NOT IN
(SELECT Name FROM Speise)
Mit typkompatiblen Relationen sind dabei nicht die Relationen **Restaurant** oder **Speise** gemeint, sondern die Ergebnisse der beiden **SELECT** Statements.
\\
\\
===== 1.7.2.4) Aggregatfunktionen und Gruppierung =====
==== Aggregatfunktionen ====
Es gibt folgende Aggregatfunktionen in SQL:
* MIN
* MAX
* SUM
* AVG
* COUNT
Bsp: Gesucht ist der Preis des teuersten Burgers:
SELECT MAX(Preis)
FROM Speise
WHERE Name LIKE '%Burger%';
Bsp.: Wie viele Speisen werden im Brauhof angeboten?
SELECT COUNT(*) //COUNT(*) zählt alle Tupel
FROM Restaurant r, Speise s,
WHERE r.rnr = s.rnr AND r.name="Brauhof";
==== Gruppierung ====
Die Select-Abfrage kann um eine Klause "**GROUP BY** //attributListe//" und eine Klausel "**HAVING** //bedingung//" ergänzt werden, um Gruppen von Tupeln zu bilden und auf diese Gruppen Aggregatfunktionen anzuwenden.
\\
Dabei werden diese Tupel nach gemeinsamen Werten in den in der **GROUP BY**-Klausel angeführten Attributen gruppiert. Von diesen Gruppen werden jene eliminiert, die die in der **HAVING**-Klausel angegebene Bedingung nicht erfüllen.
\\
\\
Anschließend werden etwaige in der **SELECT**-Klausel angegebenen Aggregatfunktionen auf jede der verbliebenen Gruppen angewendet. Diese Aggregatfunktionen (außer **COUNT**) können auch auf arithmetische Ausdrücke über Attributen angewandt werden.
\\
\\
===Beispiel===
^Relation Restaurant^^^^
^rnr^RName^Adr^Haube^
|1|McDonalds|Amstetten|0|
|2|McDonalds|Ybbs|0|
|3|Hollywood|Amstetten|1|
|4|Casa Venezia|Amstetten|2|
|5|grill.Bar|Amstetten|2|
|6|Schinakel|Grein|1|
Frage: Wie viele Restaurants haben die gleiche Haubenanzahl?
\\
\\
SELECT Haube, COUNT(*) AS AnzahlRestaurants
FROM Restaurant
GROUP BY Haube;
\\
**Ergebnis:**
\\
^Haube^AnzahlRestaurants^
|0|2|
|1|2|
|2|2|
Frage: Wie viele Restaurants in Amstetten haben die gleiche Haubenanzahl?
\\
\\
SELECT Haube, COUNT(*) AS AnzahlRestaurants
FROM Restaurant
WHERE Adr='Amstetten'
GROUP BY Haube;
\\
**Ergebnis:**
\\
^Haube^AnzahlRestaurants^
|0|1|
|1|1|
|2|2|
===Beispiel===
Gesucht ist eine SQL-Abfrage, die alle Restaurants (rnr und name) und die Anzahl der jeweils gebotenen Speisen ausgibt:
\\
^Relation Restaurant^^^^
^rnr^RName^Adr^Haube^
|1|McDonalds|Amstetten|0|
|2|McDonalds|Ybbs|0|
|3|Hollywood|Amstetten|1|
|4|Casa Venezia|Amstetten|2|
|5|grill.Bar|Amstetten|2|
|6|Schinakel|Grein|1|
^Relation Speise^^^
^rnr^SName^Preis^
|1|Hamburger|1|
|2|Hamburger|1|
|2|Cheeseburger|1,30|
|3|Hawaii|8,6|
|4|Hawaii|9|
|5|Salat|5|
|1|Salat|1,80|
SELECT r.rnr, r.name, COUNT(*) AS Anzahl
FROM Restaurant r, Speise s
WHERE r.rnr=s.rnr
GROUP BY r.rnr, r.name;
\\
**1. Schritt: JOIN **
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|5|grill.Bar|Amstetten|2|Salat|5|
**2. Schritt: GROUP BY**
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|5|grill.Bar|Amstetten|2|Salat|5|
**3. Schritt: COUNT(*) AS Anzahl (pro Gruppe) **
^rnr^RName^Anzahl^
|1|McDonalds|2|
|2|McDonalds|2|
|3|Hollywood|1|
|4|Casa Venezia|1|
|5|grill.Bar|1|
===Beispiel===
Gesucht sind alle Restaurants (rnr, name), welche mehr als 5 verschiedene (Tipp: Schlüsselwort DISTINCT) Speisen anbieten!
^Relation Restaurant^^^^
^rnr^RName^Adr^Haube^
|1|McDonalds|Amstetten|0|
|2|McDonalds|Ybbs|0|
|3|Hollywood|Amstetten|1|
|4|Casa Venezia|Amstetten|2|
|5|grill.Bar|Amstetten|2|
|6|Schinakel|Grein|1|
^Relation Speise^^^
^rnr^SName^Preis^
|1|Hamburger|1|
|2|Hamburger|1|
|2|Cheeseburger|1,30|
|3|Hawaii|8,6|
|4|Hawaii|9|
|5|Salat|5|
|1|Salat|1,80|
SELECT r.rnr, r.name
FROM Restaurant r, Speise s
WHERE r.rnr=s.rnr
GROUP BY r.rnr, r.name
HAVING COUNT(DISTINCT s.name) > 5;
**1. Schritt: JOIN **
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|5|grill.Bar|Amstetten|2|Salat|5|
**2. Schritt: GROUP BY**
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|5|grill.Bar|Amstetten|2|Salat|5|
**3. Schritt: HAVING COUNT(DISTINCT s.name)>5**
^rnr^RName^
===Beispiel===
Gesucht ist eine SQL-Abfrage, die die rnr und die Anzahl der Haube jener Restaurants angibt, deren teuerste Speise mehr als 8€ kostet. Weiters werden jeweils die Anzahl der Speisen als auch deren Durchschnittspreis ausgegeben.
^Relation Restaurant^^^^
^rnr^RName^Adr^Haube^
|1|McDonalds|Amstetten|0|
|2|McDonalds|Ybbs|0|
|3|Hollywood|Amstetten|1|
|4|Casa Venezia|Amstetten|2|
|5|grill.Bar|Amstetten|2|
|6|Schinakel|Grein|1|
^Relation Speise^^^
^rnr^SName^Preis^
|1|Hamburger|1|
|2|Hamburger|1|
|2|Cheeseburger|1,30|
|3|Hawaii|8,6|
|4|Hawaii|9|
|5|Salat|5|
|1|Salat|1,80|
|4|Hühnerstreifensalat|8,20|
SELECT r.rnr, r.haube, COUNT(DISTINCT s.name) AS Anzahl, AVG(s.preis) AS AVGPreis
FROM Restaurant r, Speise s
WHERE r.rnr=s.rnr
GROUP BY r.rnr, haube
HAVING MAX (s.preis)>8;
**1. Schritt: JOIN **
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|4|Casa Venezia|Amstetten|2|Hühnerstreifensalat|7,20|
|5|grill.Bar|Amstetten|2|Salat|5|
**2. Schritt: GROUP BY**
|1|McDonalds|Amstetten|0|Hamburger|1|
|1|McDonalds|Amstetten|0|Salat|1,80|
|2|McDonalds|Ybbs|0|Hamburger|1|
|2|McDonalds|Ybbs|0|Cheeseburger|1,30|
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|4|Casa Venezia|Amstetten|2|Hühnerstreifensalat|7,20|
|5|grill.Bar|Amstetten|2|Salat|5|
**3. Schritt: HAVING MAX(s.preis)>8**
|3|Hollywood|Amstetten|1|Hawaii|8,6|
|4|Casa Venezia|Amstetten|2|Hawaii|9|
|4|Casa Venezia|Amstetten|2|Hühnerstreifensalat|7,20|
**4. Schritt: SELECT r.rnr, r.name, COUNT(DISTINCT s.name) AS Anzahl, AVG(s.preis) AS AVGPreis **
^rnr^RName^Anzahl^Preis^
|3|Hollywood|1|8,6|
|4|Casa Venezia|2|8,1|