Inhaltsverzeichnis
- Aufbau der SELECT-Anweisung
- Spalten auswählen und Aliase
- Arithmetische Ausdrücke
- WHERE – Zeilen filtern
- Vergleichs- und logische Operatoren
- Spezielle WHERE-Operatoren
- ORDER BY – Sortieren
- Einzeilige Funktionen
- NULL-Behandlung
- GROUP BY und Aggregatfunktionen
- HAVING – Gruppen filtern
- Zusammenfassung und Ausblick
DQL – Data Query Language: Die SELECT-Anweisung
Fach: Informationstechnologie – Datenbanksysteme
Schulstufe: 11. Schulstufe – HTL Informatik
Voraussetzungen: SQL Einführung, SCOTT-Schema, Oracle SQL Developer
Autor: HTL Pinkafeld – IF/IT
1. Aufbau der SELECT-Anweisung
1.1 Grundstruktur
Die SELECT-Anweisung ist die wichtigste SQL-Anweisung. Sie fragt Daten aus einer oder mehreren Tabellen ab:
SELECT spalte1, spalte2, ... -- Welche Spalten?
FROM tabellenname -- Aus welcher Tabelle?
WHERE bedingung -- Welche Zeilen? (optional)
ORDER BY spalte; -- Sortierung (optional)
1.2 Verarbeitungsreihenfolge
Wichtig: SQL-Klauseln werden nicht in der Reihenfolge ausgewertet, in der sie geschrieben werden:
Schreibreihenfolge: Auswertungsreihenfolge (intern):
1. SELECT 1. FROM -- Welche Tabelle?
2. FROM 2. WHERE -- Welche Zeilen?
3. WHERE 3. GROUP BY -- Gruppen bilden
4. GROUP BY 4. HAVING -- Gruppen filtern
5. HAVING 5. SELECT -- Spalten berechnen
6. ORDER BY 6. ORDER BY -- Sortieren
1.3 Alle Spalten und alle Zeilen
-- Alle Spalten mit * (Stern)
SELECT * FROM EMP;
-- Alle Spalten explizit (besser für Produktion – robuster bei Schema-Änderungen)
SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
FROM EMP;
Ergebnis (Ausschnitt):
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ------ --------- ---- ----------- ---- ---- ------
7369 SMITH CLERK 7902 17.12.1980 800 20
7499 ALLEN SALESMAN 7698 20.02.1981 1600 300 30
7839 KING PRESIDENT 17.11.1981 5000 10
2. Spalten auswählen und Aliase
2.1 Bestimmte Spalten auswählen
-- Nur Name und Gehalt
SELECT ENAME, SAL
FROM EMP;
-- Reihenfolge bestimmt die Ausgabereihenfolge
SELECT DEPTNO, ENAME, JOB
FROM EMP;
2.2 DISTINCT – Duplikate entfernen
-- Alle verschiedenen Berufsbezeichnungen
SELECT DISTINCT JOB
FROM EMP;
-- Ergebnis:
-- JOB
-- ---------
-- ANALYST
-- CLERK
-- MANAGER
-- PRESIDENT
-- SALESMAN
-- DISTINCT gilt für die gesamte Spaltenauswahl
SELECT DISTINCT JOB, DEPTNO
FROM EMP;
2.3 Spalten-Aliase
Aliase geben Spalten eigene Bezeichnungen in der Ausgabe:
-- Mit AS (empfohlen)
SELECT ENAME AS "Mitarbeiter",
SAL AS "Gehalt in USD"
FROM EMP;
-- Ohne AS (kürzer, aber weniger lesbar)
SELECT ENAME "Mitarbeiter",
SAL Monatsgehalt
FROM EMP;
Regel: Aliase mit Sonderzeichen, Leerzeichen oder Groß-/Kleinschreibung müssen in doppelten Anführungszeichen stehen. Ohne Anführungszeichen schreibt Oracle den Alias automatisch in Großbuchstaben.
2.4 Tabellen-Aliase
-- Tabellenalias (besonders wichtig bei mehreren Tabellen / Joins)
SELECT e.ENAME, e.SAL, e.DEPTNO
FROM EMP e;
3. Arithmetische Ausdrücke
3.1 Berechnungen in SELECT
Oracle erlaubt arithmetische Ausdrücke direkt in der SELECT-Liste:
-- Jahresgehalt berechnen
SELECT ENAME,
SAL,
SAL * 12 AS "Jahresgehalt",
SAL * 12 + 1000 AS "Jahresgehalt + Bonus"
FROM EMP;
Operatoren: +, -, *, / (Klammerung möglich)
3.2 Operator-Priorität
-- Multiplikation vor Addition (wie in Mathematik)
SELECT SAL + SAL * 0.2 AS falsch_weil_mehrdeutig,
SAL * 1.2 AS richtig,
(SAL + 500) * 12 AS mit_klammern
FROM EMP;
3.3 NULL in Berechnungen
-- NULL in einer Berechnung ergibt immer NULL!
SELECT ENAME,
SAL,
COMM,
SAL + COMM AS "Gesamt (fehlerhaft)"
FROM EMP;
-- Ergebnis für SMITH (COMM ist NULL):
-- ENAME SAL COMM Gesamt
-- SMITH 800 NULL NULL <-- nicht 800!
Merke: Jede arithmetische Operation mit NULL ergibt NULL. Lösung: NVL-Funktion (siehe Kapitel 9).
4. WHERE – Zeilen filtern
4.1 Einfache Bedingungen
-- Mitarbeiter der Abteilung 20
SELECT ENAME, JOB, DEPTNO
FROM EMP
WHERE DEPTNO = 20;
-- Mitarbeiter mit Gehalt über 2000
SELECT ENAME, SAL
FROM EMP
WHERE SAL > 2000;
-- Genau ein Mitarbeiter nach Name
SELECT *
FROM EMP
WHERE ENAME = 'KING'; -- Zeichenketten in EINFACHEN Anführungszeichen!
-- Oracle: Groß-/Kleinschreibung beachten!
Wichtig: In Oracle sind Zeichenketten case-sensitive.
'king'und'KING'sind verschieden!
4.2 Datumsvergleiche
-- Eingestellt nach dem 01.01.1982
SELECT ENAME, HIREDATE
FROM EMP
WHERE HIREDATE > TO_DATE('01.01.1982', 'DD.MM.YYYY');
-- Im Jahr 1981 eingestellt
SELECT ENAME, HIREDATE
FROM EMP
WHERE HIREDATE >= TO_DATE('01.01.1981', 'DD.MM.YYYY')
AND HIREDATE < TO_DATE('01.01.1982', 'DD.MM.YYYY');
5. Vergleichs- und logische Operatoren
5.1 Vergleichsoperatoren
| Operator | Bedeutung | Beispiel |
|---|---|---|
= |
Gleich | SAL = 3000 |
<> oder != |
Ungleich | JOB <> 'CLERK' |
< |
Kleiner als | SAL < 1500 |
> |
Größer als | SAL > 2000 |
<= |
Kleiner oder gleich | SAL <= 1000 |
>= |
Größer oder gleich | HIREDATE >= ... |
5.2 Logische Operatoren
-- AND: beide Bedingungen müssen erfüllt sein
SELECT ENAME, JOB, SAL
FROM EMP
WHERE JOB = 'SALESMAN'
AND SAL > 1400;
-- OR: mindestens eine Bedingung muss erfüllt sein
SELECT ENAME, JOB
FROM EMP
WHERE JOB = 'PRESIDENT'
OR JOB = 'ANALYST';
-- NOT: Bedingung umkehren
SELECT ENAME, DEPTNO
FROM EMP
WHERE NOT DEPTNO = 20;
-- entspricht: WHERE DEPTNO <> 20
5.3 Operator-Priorität
Priorität (absteigend):
1. Arithmetische Operatoren (* / + -)
2. Vergleichsoperatoren (= <> < > <= >=)
3. NOT
4. AND
5. OR
-- Achtung: AND bindet stärker als OR!
SELECT ENAME, JOB, DEPTNO
FROM EMP
WHERE JOB = 'CLERK' AND DEPTNO = 20
OR JOB = 'ANALYST';
-- = (JOB='CLERK' AND DEPTNO=20) OR (JOB='ANALYST')
-- Mit Klammern explizit steuern:
WHERE JOB = 'CLERK' AND (DEPTNO = 20 OR DEPTNO = 10)
6. Spezielle WHERE-Operatoren
6.1 BETWEEN ... AND
Prüft einen Wert auf einen Bereich (inklusiv):
-- Gehalt zwischen 1000 und 2000 (inklusiv)
SELECT ENAME, SAL
FROM EMP
WHERE SAL BETWEEN 1000 AND 2000;
-- Äquivalent zu:
WHERE SAL >= 1000 AND SAL <= 2000
-- Auch für Datum:
SELECT ENAME, HIREDATE
FROM EMP
WHERE HIREDATE BETWEEN TO_DATE('01.01.1981','DD.MM.YYYY')
AND TO_DATE('31.12.1981','DD.MM.YYYY');
-- NOT BETWEEN: außerhalb des Bereichs
WHERE SAL NOT BETWEEN 1000 AND 2000
6.2 IN (...)
Prüft ob ein Wert in einer Liste enthalten ist:
-- Bestimmte Berufe
SELECT ENAME, JOB
FROM EMP
WHERE JOB IN ('CLERK', 'ANALYST', 'MANAGER');
-- Äquivalent zu:
WHERE JOB = 'CLERK' OR JOB = 'ANALYST' OR JOB = 'MANAGER'
-- Bestimmte Abteilungen
SELECT ENAME, DEPTNO
FROM EMP
WHERE DEPTNO IN (10, 20);
-- NOT IN: nicht in der Liste
SELECT ENAME, JOB
FROM EMP
WHERE JOB NOT IN ('CLERK', 'SALESMAN');
6.3 LIKE – Mustervergleich
Sucht nach Zeichenmustern mit Platzhaltern:
| Platzhalter | Bedeutung | Beispiel |
|---|---|---|
% |
Beliebig viele Zeichen (auch 0) | 'S%' = beginnt mit S |
_ |
Genau ein beliebiges Zeichen | '_A%' = 2. Zeichen ist A |
-- Name beginnt mit 'S'
SELECT ENAME FROM EMP WHERE ENAME LIKE 'S%';
-- SMITH, SCOTT
-- Name endet auf 'N'
SELECT ENAME FROM EMP WHERE ENAME LIKE '%N';
-- ALLEN, MARTIN
-- Name enthält 'AR'
SELECT ENAME FROM EMP WHERE ENAME LIKE '%AR%';
-- WARD, MARTIN, CLARK
-- Genau 4 Zeichen lang
SELECT ENAME FROM EMP WHERE ENAME LIKE '____';
-- WARD, FORD, KING
-- Zweiter Buchstabe ist 'L'
SELECT ENAME FROM EMP WHERE ENAME LIKE '_L%';
-- ALLEN, CLARK, BLAKE
-- NOT LIKE
SELECT ENAME FROM EMP WHERE ENAME NOT LIKE 'S%';
Groß-/Kleinschreibung: LIKE ist in Oracle case-sensitive.
LIKE 's%'findet keinen Mitarbeiter, da alle Namen in Großbuchstaben gespeichert sind.
6.4 IS NULL / IS NOT NULL
-- Mitarbeiter ohne Provision (COMM ist NULL)
SELECT ENAME, COMM
FROM EMP
WHERE COMM IS NULL;
-- Mitarbeiter mit Provision (COMM ist nicht NULL)
SELECT ENAME, COMM
FROM EMP
WHERE COMM IS NOT NULL;
-- FALSCH! = NULL ergibt nie TRUE
-- WHERE COMM = NULL -- findet keine Zeilen!
-- WHERE COMM <> NULL -- findet keine Zeilen!
7. ORDER BY – Sortieren
7.1 Grundlegende Sortierung
-- Aufsteigend (ASC ist Standard, kann weggelassen werden)
SELECT ENAME, SAL
FROM EMP
ORDER BY SAL;
-- Absteigend
SELECT ENAME, SAL
FROM EMP
ORDER BY SAL DESC;
-- Nach Name alphabetisch
SELECT ENAME, JOB
FROM EMP
ORDER BY ENAME ASC;
7.2 Mehrere Sortierspalten
-- Primär nach Abteilung, innerhalb gleicher Abteilung nach Gehalt absteigend
SELECT DEPTNO, ENAME, SAL
FROM EMP
ORDER BY DEPTNO ASC, SAL DESC;
-- Ergebnis:
-- DEPTNO ENAME SAL
-- ------ ------ ----
-- 10 KING 5000
-- 10 CLARK 2450
-- 10 MILLER 1300
-- 20 FORD 3000
-- 20 SCOTT 3000
-- ...
7.3 Sortierung nach Spaltennummer
-- Sortierung nach der 2. und 3. Spalte der SELECT-Liste
SELECT ENAME, DEPTNO, SAL
FROM EMP
ORDER BY 2, 3 DESC;
-- = ORDER BY DEPTNO, SAL DESC
7.4 NULL-Sortierung
-- In Oracle stehen NULLs standardmäßig am Ende (ASC) bzw. am Anfang (DESC)
SELECT ENAME, COMM
FROM EMP
ORDER BY COMM; -- NULLs kommen zuletzt
-- NULLS FIRST / NULLS LAST explizit steuern
ORDER BY COMM NULLS FIRST; -- NULLs zuerst
ORDER BY COMM NULLS LAST; -- NULLs zuletzt
7.5 Sortierung nach Alias
SELECT ENAME,
SAL * 12 AS jahresgehalt
FROM EMP
ORDER BY jahresgehalt DESC; -- Alias aus SELECT verwenden
8. Einzeilige Funktionen
Einzeilige Funktionen verarbeiten eine Zeile und geben einen Wert zurück.
8.1 Zeichenkettenfunktionen
-- UPPER / LOWER / INITCAP
SELECT UPPER('hello'), -- HELLO
LOWER('WORLD'), -- world
INITCAP('hello world') -- Hello World
FROM DUAL;
-- Suche case-insensitiv
SELECT ENAME FROM EMP
WHERE UPPER(ENAME) = 'SMITH';
-- LENGTH: Länge einer Zeichenkette
SELECT ENAME, LENGTH(ENAME) AS laenge
FROM EMP
ORDER BY laenge DESC;
-- SUBSTR(string, start, laenge)
-- Zählung beginnt bei 1!
SELECT SUBSTR('Oracle SQL', 1, 6), -- Oracle
SUBSTR('Oracle SQL', 8), -- SQL (bis Ende)
SUBSTR('Oracle SQL', -3) -- SQL (von hinten)
FROM DUAL;
-- INSTR(string, suchstring): Position des ersten Vorkommens
SELECT INSTR('Oracle SQL', 'SQL') -- 8
FROM DUAL;
-- CONCAT oder ||: Zeichenketten verbinden
SELECT CONCAT(ENAME, ' ist ' ) || JOB AS beschreibung
FROM EMP;
-- SMITH ist CLERK
-- TRIM / LTRIM / RTRIM: Leerzeichen entfernen
SELECT TRIM(' Hello '), -- 'Hello'
LTRIM(' Hello '), -- 'Hello '
RTRIM(' Hello ') -- ' Hello'
FROM DUAL;
-- LPAD / RPAD: Auffüllen auf bestimmte Länge
SELECT LPAD(SAL, 10, '*'), -- *****3000
RPAD(ENAME, 12, '.') -- SMITH.......
FROM EMP;
-- REPLACE: Zeichen ersetzen
SELECT REPLACE('HELLO WORLD', 'O', '0') -- HELL0 W0RLD
FROM DUAL;
8.2 Numerische Funktionen
-- ROUND: Runden
SELECT ROUND(123.456, 2), -- 123.46
ROUND(123.456, 0), -- 123
ROUND(123.456, -1) -- 120
FROM DUAL;
-- TRUNC: Abschneiden (kein Runden!)
SELECT TRUNC(123.456, 2), -- 123.45
TRUNC(123.456, 0), -- 123
TRUNC(123.456, -1) -- 120
FROM DUAL;
-- MOD: Modulo (Rest der Ganzzahldivision)
SELECT MOD(17, 5), -- 2
MOD(10, 2) -- 0 (gerade Zahl)
FROM DUAL;
-- ABS: Absolutwert
SELECT ABS(-250) -- 250
FROM DUAL;
-- CEIL / FLOOR: Aufrunden / Abrunden auf ganze Zahl
SELECT CEIL(4.1), -- 5
FLOOR(4.9) -- 4
FROM DUAL;
-- POWER / SQRT
SELECT POWER(2, 10), -- 1024
SQRT(144) -- 12
FROM DUAL;
8.3 Datumsfunktionen
-- Aktuelles Datum
SELECT SYSDATE FROM DUAL; -- 15.06.2025 10:30:00
SELECT TRUNC(SYSDATE) FROM DUAL; -- 15.06.2025 00:00:00 (nur Datum)
-- Datumsarithmetik (Einheit: Tage)
SELECT SYSDATE + 7 AS naechste_woche,
SYSDATE - 30 AS vor_30_tagen,
HIREDATE + 365 AS nach_einem_jahr
FROM EMP;
-- Differenz zwischen Daten (Ergebnis in Tagen)
SELECT ENAME,
SYSDATE - HIREDATE AS tage_im_unternehmen,
ROUND((SYSDATE - HIREDATE) / 365, 1) AS jahre
FROM EMP;
-- MONTHS_BETWEEN: Differenz in Monaten
SELECT ENAME,
ROUND(MONTHS_BETWEEN(SYSDATE, HIREDATE), 1) AS monate
FROM EMP;
-- ADD_MONTHS: Monate addieren
SELECT HIREDATE,
ADD_MONTHS(HIREDATE, 6) AS plus_6_monate,
ADD_MONTHS(HIREDATE, -3) AS minus_3_monate
FROM EMP;
-- LAST_DAY: Letzter Tag des Monats
SELECT SYSDATE,
LAST_DAY(SYSDATE) AS letzter_tag
FROM DUAL;
-- NEXT_DAY: Nächster Wochentag
SELECT NEXT_DAY(SYSDATE, 'MONDAY') AS naechster_montag
FROM DUAL;
-- EXTRACT: Teile extrahieren
SELECT EXTRACT(YEAR FROM HIREDATE) AS jahr,
EXTRACT(MONTH FROM HIREDATE) AS monat,
EXTRACT(DAY FROM HIREDATE) AS tag
FROM EMP;
8.4 Datentypkonvertierung
-- TO_CHAR: Zahl / Datum → Zeichenkette
SELECT TO_CHAR(SYSDATE, 'DD.MM.YYYY') AS datum,
TO_CHAR(SYSDATE, 'DD.MM.YYYY HH24:MI') AS datum_zeit,
TO_CHAR(SYSDATE, 'DAY') AS wochentag,
TO_CHAR(SYSDATE, 'Month YYYY') AS monat_jahr
FROM DUAL;
SELECT TO_CHAR(SAL, '99,999.00') AS formatiertes_gehalt,
TO_CHAR(SAL, 'L99G999D99') AS mit_waehrung
FROM EMP;
-- TO_DATE: Zeichenkette → Datum
SELECT TO_DATE('25.12.2025', 'DD.MM.YYYY') AS weihnachten,
TO_DATE('2025-12-25', 'YYYY-MM-DD') AS iso_datum
FROM DUAL;
-- TO_NUMBER: Zeichenkette → Zahl
SELECT TO_NUMBER('1.234,56', '9G999D99') AS zahl
FROM DUAL;
Wichtige Formatmasken:
| Maske | Bedeutung | Beispiel |
|---|---|---|
DD |
Tag zweistellig | 05 |
MM |
Monat zweistellig | 06 |
YYYY |
Jahr vierstellig | 2025 |
HH24 |
Stunde (0–23) | 14 |
MI |
Minute | 30 |
SS |
Sekunde | 45 |
DAY |
Wochentagname | MONDAY |
MONTH |
Monatsname | JUNE |
9. NULL-Behandlung
9.1 NVL – NULL ersetzen
-- NVL(ausdruck, ersatzwert): Falls NULL → Ersatzwert
SELECT ENAME,
COMM,
NVL(COMM, 0) AS comm_ohne_null,
SAL + NVL(COMM, 0) AS gesamtverguetung
FROM EMP;
-- Mit Zeichenketten
SELECT ENAME,
NVL(TO_CHAR(COMM), 'keine Provision') AS provision
FROM EMP;
9.2 NVL2 – Fallunterscheidung bei NULL
-- NVL2(ausdruck, wert_wenn_nicht_null, wert_wenn_null)
SELECT ENAME,
NVL2(COMM, 'Hat Provision', 'Keine Provision') AS status,
NVL2(COMM, SAL + COMM, SAL) AS gesamtgehalt
FROM EMP;
9.3 COALESCE – Erster Nicht-NULL-Wert
-- COALESCE gibt den ersten Nicht-NULL-Wert zurück
SELECT ENAME,
COALESCE(COMM, SAL * 0.1, 0) AS bonus
FROM EMP;
-- Wenn COMM nicht NULL → COMM
-- Wenn COMM NULL aber SAL*0.1 nicht NULL → SAL*0.1
-- Wenn beide NULL → 0
9.4 NULLIF – NULL wenn gleich
-- NULLIF(a, b): Wenn a = b → NULL, sonst a
SELECT ENAME,
NULLIF(COMM, 0) AS comm_bereinigt
FROM EMP;
-- Provision von 0 wird zu NULL (Turner hatte COMM=0)
10. GROUP BY und Aggregatfunktionen
10.1 Aggregatfunktionen
Aggregatfunktionen berechnen einen Wert für mehrere Zeilen:
-- Ohne GROUP BY: über alle Zeilen
SELECT COUNT(*) AS anzahl_mitarbeiter,
SUM(SAL) AS summe_gehaelter,
AVG(SAL) AS durchschnittsgehalt,
MAX(SAL) AS hoechstes_gehalt,
MIN(SAL) AS niedrigstes_gehalt,
MAX(HIREDATE) AS spaeteste_einstellung
FROM EMP;
10.2 COUNT – Anzahl zählen
-- Anzahl Zeilen (inkl. NULL)
SELECT COUNT(*) FROM EMP; -- 14
-- Anzahl nicht-NULL-Werte in COMM
SELECT COUNT(COMM) FROM EMP; -- 4 (nur Salesman haben COMM)
-- Anzahl verschiedener Berufe
SELECT COUNT(DISTINCT JOB) FROM EMP; -- 5
10.3 GROUP BY – Nach Gruppen aggregieren
-- Durchschnittsgehalt pro Abteilung
SELECT DEPTNO,
COUNT(*) AS anzahl,
AVG(SAL) AS avg_gehalt,
MAX(SAL) AS max_gehalt,
SUM(SAL) AS sum_gehalt
FROM EMP
GROUP BY DEPTNO
ORDER BY DEPTNO;
-- Ergebnis:
-- DEPTNO ANZAHL AVG_GEHALT MAX_GEHALT SUM_GEHALT
-- ------ ------ ---------- ---------- ----------
-- 10 3 2916.67 5000 8750
-- 20 5 2175.00 3000 10875
-- 30 6 1566.67 2850 9400
10.4 GROUP BY Regeln
Wichtige Regel: Jede Spalte in SELECT, die keine Aggregatfunktion ist, muss in GROUP BY stehen!
-- RICHTIG:
SELECT DEPTNO, JOB, COUNT(*), AVG(SAL)
FROM EMP
GROUP BY DEPTNO, JOB;
-- FALSCH: ENAME ist nicht in GROUP BY und keine Aggregatfunktion
SELECT DEPTNO, ENAME, COUNT(*)
FROM EMP
GROUP BY DEPTNO;
-- ORA-00979: not a GROUP BY expression
10.5 GROUP BY mit mehreren Spalten
-- Pro Abteilung und Beruf
SELECT DEPTNO,
JOB,
COUNT(*) AS anzahl,
SUM(SAL) AS summe
FROM EMP
GROUP BY DEPTNO, JOB
ORDER BY DEPTNO, JOB;
-- Ergebnis:
-- DEPTNO JOB ANZAHL SUMME
-- ------ --------- ------ -----
-- 10 CLERK 1 1300
-- 10 MANAGER 1 2450
-- 10 PRESIDENT 1 5000
-- 20 ANALYST 2 6000
-- ...
11. HAVING – Gruppen filtern
11.1 Unterschied WHERE / HAVING
WHERE: Filtert einzelne Zeilen VOR dem Gruppieren
Darf keine Aggregatfunktionen enthalten
HAVING: Filtert Gruppen NACH dem Gruppieren
Darf Aggregatfunktionen enthalten
-- HAVING: Nur Abteilungen mit mehr als 3 Mitarbeitern
SELECT DEPTNO,
COUNT(*) AS anzahl,
AVG(SAL) AS avg_gehalt
FROM EMP
GROUP BY DEPTNO
HAVING COUNT(*) > 3;
-- Kombination WHERE + GROUP BY + HAVING
-- Nur CLERK und SALESMAN, dann nur Gruppen mit Durchschnittsgehalt > 1000
SELECT JOB,
COUNT(*) AS anzahl,
AVG(SAL) AS avg_gehalt
FROM EMP
WHERE JOB IN ('CLERK', 'SALESMAN') -- Erst Zeilen filtern
GROUP BY JOB -- Dann gruppieren
HAVING AVG(SAL) > 1000 -- Dann Gruppen filtern
ORDER BY avg_gehalt DESC; -- Dann sortieren
11.2 Typische Fehler und Korrekturen
-- FALSCH: Aggregatfunktion in WHERE
SELECT DEPTNO, AVG(SAL)
FROM EMP
WHERE AVG(SAL) > 2000 -- ORA-00934: group function is not allowed here
GROUP BY DEPTNO;
-- RICHTIG: Aggregatfunktion in HAVING
SELECT DEPTNO, AVG(SAL)
FROM EMP
GROUP BY DEPTNO
HAVING AVG(SAL) > 2000;
-- FALSCH: Nicht-Aggregat-Spalte ohne GROUP BY
SELECT DEPTNO, COUNT(*)
FROM EMP; -- ORA-00937: not a single-group group function
-- RICHTIG:
SELECT DEPTNO, COUNT(*)
FROM EMP
GROUP BY DEPTNO;
11.3 Vollständige SELECT-Struktur
SELECT spalten / aggregatfunktionen -- 5. Was ausgeben?
FROM tabelle -- 1. Woher?
WHERE zeilenbedingung -- 2. Welche Zeilen?
GROUP BY gruppierungsspalten -- 3. Wie gruppieren?
HAVING gruppenbedingung -- 4. Welche Gruppen?
ORDER BY sortierspalten; -- 6. Wie sortieren?
12. Zusammenfassung und Ausblick
DQL-Überblick
-- Vollständiges Beispiel:
-- Berufe mit mehr als 1 Mitarbeiter, ohne PRESIDENT,
-- sortiert nach Durchschnittsgehalt absteigend
SELECT JOB,
COUNT(*) AS anzahl,
ROUND(AVG(SAL)) AS avg_gehalt,
MIN(SAL) AS min_gehalt,
MAX(SAL) AS max_gehalt
FROM EMP
WHERE JOB <> 'PRESIDENT'
GROUP BY JOB
HAVING COUNT(*) > 1
ORDER BY avg_gehalt DESC;
Checkliste DQL
SELECT-Grundstruktur und Auswertungsreihenfolge kennen
DISTINCT korrekt einsetzen
Aliase mit/ohne AS und Anführungszeichen
Arithmetische Ausdrücke und NULL-Problem
WHERE-Operatoren: =, <>, <, >, BETWEEN, IN, LIKE, IS NULL
AND / OR / NOT und Priorität (Klammern!)
ORDER BY: ASC/DESC, mehrere Spalten, NULLS FIRST/LAST
Zeichenkettenfunktionen: UPPER/LOWER, SUBSTR, LENGTH, ...
Numerische Funktionen: ROUND, TRUNC, MOD
Datumsfunktionen: SYSDATE, ADD_MONTHS, MONTHS_BETWEEN
Konvertierung: TO_CHAR, TO_DATE, TO_NUMBER
NULL-Funktionen: NVL, NVL2, COALESCE, NULLIF
Aggregatfunktionen: COUNT, SUM, AVG, MAX, MIN
GROUP BY: Regel – alle Nicht-Aggregat-Spalten in GROUP BY!
HAVING: Unterschied zu WHERE
Typische Prüfungsaufgaben
-- 1. Alle Mitarbeiter der Abteilung 20, sortiert nach Gehalt absteigend
SELECT ENAME, JOB, SAL
FROM EMP
WHERE DEPTNO = 20
ORDER BY SAL DESC;
-- 2. Jahresgehalt inkl. Provision für alle Mitarbeiter
SELECT ENAME,
SAL * 12 + NVL(COMM, 0) * 12 AS jahresverguetung
FROM EMP
ORDER BY jahresverguetung DESC;
-- 3. Abteilungen mit durchschnittlichem Gehalt über 2500
SELECT DEPTNO, ROUND(AVG(SAL), 2) AS avg_sal
FROM EMP
GROUP BY DEPTNO
HAVING AVG(SAL) > 2500;
-- 4. Anzahl Mitarbeiter pro Beruf, nur Berufe mit mehr als 2 Personen
SELECT JOB, COUNT(*) AS anzahl
FROM EMP
GROUP BY JOB
HAVING COUNT(*) > 2
ORDER BY anzahl DESC;
-- 5. Mitarbeiter die im Jahr 1981 eingestellt wurden
SELECT ENAME, HIREDATE
FROM EMP
WHERE EXTRACT(YEAR FROM HIREDATE) = 1981
ORDER BY HIREDATE;
Ausblick: Nächste Themen
- Joins: Mehrere Tabellen verknüpfen (INNER JOIN, OUTER JOIN, SELF JOIN)
- Subqueries: Unterabfragen in WHERE und FROM
- DML: INSERT, UPDATE, DELETE, MERGE
- DDL: CREATE TABLE, ALTER TABLE, Constraints
- PL/SQL: Prozeduren, Funktionen, Trigger
HTL Pinkafeld – IF/IT | Oracle SQL | 11. Schulstufe