===== 9.7.2) DQL - Data Query Language ===== ===== 9.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; ===== 9.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%'; ===== 9.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) Mit typkompatiblen Relationen sind dabei nicht die Relationen **Restaurant** oder **Speise** gemeint, sondern die Ergebnisse der beiden **SELECT** Statements. \\ \\ ===== 9.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| */