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


HTL Pinkafeld – IF/IT | Oracle SQL | 11. Schulstufe

Vorheriges ThemaEinführung & RDBMS Nächstes ThemaJoins