SQL-Beispiele aus dem Miro-Board (04 SQL-Vertiefung, Fortsetzung)
Subqueries
SELECT COUNT(*) FROM ware;
SELECT sin((SELECT COUNT(*) FROM ware)); -- nicht sehr sinnvoll
-- SELECT sin(SELECT COUNT(*) FROM ware); -- Klammer um Argument fehlt
SELECT EXISTS (SELECT * FROM ware WHERE w_bezeichnung = 'Eieruhr');
SELECT ('Eieruhr', 'Sonstiges') IN (SELECT w_bezeichnung, w_typ FROM ware);
SELECT ROW('Eieruhr', 'Sonstiges') IN (SELECT w_bezeichnung, w_typ FROM ware);
Select-Klausel
/***************************************************************************
* Sammeln von eindeutigen Informationen aus verschiedenen Tabellen.
* Das folgende Beispiel ist nicht sonderlich sinnvoll, aber korrekt.
***************************************************************************/
SELECT
(SELECT h_name FROM haendler WHERE h_id = 1) AS haendler,
(SELECT w_bezeichnung FROM ware WHERE w_id = 3) AS ware,
(SELECT w_bezeichnung FROM ware WHERE w_id = 999) AS ware
--FROM one
;
-- Funktioniert nicht, da es mehrere Händler gibt:
SELECT
(SELECT h_name FROM haendler WHERE h_id > 1) AS haendler
;
-- Funktioniert nicht, da es mehrere Attribute gibt:
SELECT
(SELECT h_name, h_ortschaft FROM haendler WHERE h_id = 1) AS haendler
;
-- Funktioniert, da es nur ein Attribut gibt:
SELECT
(SELECT ROW(h_name, h_ortschaft) FROM haendler WHERE h_id = 1) AS haendler
;
Korrelierte vs. nicht-korrelierte Subquery
/***************************************************************************
* Wie viel Prozent des gesamten Warenangebots liefert jeder Händler?
* Beachte: Man muss 100.0 an Stelle von 100 schreiben, um eine Float-
* Division an Stelle einer Integer-Division zu erzwingen.
***************************************************************************/
SELECT h.h_id, h.h_name, h.h_ortschaft,
-- absolute Zahl der Waren, die Händler h liefert
COUNT(DISTINCT l.w_id)
AS anzahl_waren_je_haendler,
-- prozentualer Anteil der Waren, die Händler h liefert
-- (mit Subquery zur Ermittlung der Anzahl aller Waren)
ROUND(100.0*COUNT(DISTINCT l.w_id)/(SELECT COUNT(*) FROM ware), 2)
AS prozent_waren
FROM haendler h LEFT JOIN liefert l ON h.h_id = l.h_id
GROUP BY h.h_id, h.h_name, h.h_ortschaft
ORDER BY prozent_waren DESC, h_id
;
SELECT h_id,
(SELECT COUNT(DISTINCT w_id) -- korrelierte Subquery
FROM liefert l
WHERE l.h_id = h.h_id -- Zugriff auf h.h_id,
) AS h_anzahl_waren, -- außerhalb der Subquery definiert
(SELECT COUNT(w_id) -- nicht-korrelierte Subquery
FROM ware
) AS w_anzahl_alle_waren
FROM haendler h
;
Durchnummerieren
/***************************************************************************
* Jede Ware soll mit einer laufenden Nummer versehen werden, in der
* alphabethischen Reihenfolge der Bezeichnungen:
***************************************************************************/
SELECT (SELECT COUNT(*)+1
FROM ware w2
WHERE w2.w_bezeichnung < w1.w_bezeichnung
) AS nr,
w1.w_bezeichnung
FROM ware w1
ORDER BY nr
;
-- Besser (da performanter): Window-Funktion verwenden:
SELECT COUNT(*) OVER (ORDER BY w_bezeichnung) AS nr,
w_bezeichnung
FROM ware
ORDER BY nr
;
-- oder
SELECT RANK() OVER (ORDER BY w_bezeichnung) AS nr,
w_bezeichnung
FROM ware
ORDER BY nr
;
SELECT DENSE_RANK() OVER (ORDER BY w_bezeichnung) AS nr,
w_bezeichnung
FROM ware
ORDER BY nr
;
From-Klausel
/***************************************************************************
* Sinnloses, aber einfaches Beispiel
***************************************************************************/
-- SQL-Standard-konform, aber funktioniert in PostgreSQL nicht
/*
SELECT h_id
FROM (SELECT DISTINCT h_id, w_id FROM liefert)
;
*/
-- PostgreSQL: Man muss für eine Subquery in einer From-Klause
-- einen Alias-Namen vergeben!
SELECT h_id
FROM (SELECT DISTINCT h_id, w_id FROM liefert) AS dummy
;
SELECT h_id
FROM (SELECT DISTINCT h_id, w_id FROM liefert) _
;
Weitere Beispiele
/*******************************************************************************
* Wie viele verschiedene Waren liefern die jeweiligen Händler?
* Beachten Sie, dass eine Händler ein und dieselbe Ware
* mehrfach zu unterschiedlichen Konditionen liefern kann!
*******************************************************************************/
SELECT h_id, COUNT(*) AS l_anzahl_waren
FROM (SELECT DISTINCT h_id, w_id FROM liefert) AS _
GROUP BY h_id
ORDER BY h_id
;
-- Das funktioniert allerdings auch ohne Subquery!
SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert
GROUP BY h_id
ORDER BY h_id
;
-- Es geht auch ohne GROUP BY mit einer Subquery in der SELECT-Klausel.
SELECT h_id,
(SELECT COUNT(DISTINCT w_id)
FROM liefert l
WHERE l.h_id = h.h_id
) AS l_anzahl_waren
FROM haendler h
;
/* Ohne DISTINCT wäre das Ergebnis fehlerhaft!
Zum Beispiel liefert Händler 4 ein und dieselbe Ware zu
unterschiedlichen Preisen und Konditionen. Gefragt war
aber die Anzahl der UNTERSCHIEDLICHEN Waren, die ein
Händler liefert.
*/
Weitere Beispiele (Fortsetzung)
/* Das Ganze noch einmal mit Angabe von Händlernamen und -ortschaft. */
SELECT h.h_id, h.h_name, h.h_ortschaft, COUNT(w_id) AS l_anzahl_waren
FROM haendler h
LEFT JOIN (SELECT DISTINCT h_id, w_id FROM liefert) l
ON h.h_id = l.h_id
GROUP BY h.h_id, h.h_name, h.h_ortschaft
ORDER BY h_id
;
/* Die Subquery kann man auch mit einer (temporären) View berechnen */
WITH l(h_id, w_id)
AS NOT MATERIALIZED
(SELECT DISTINCT h_id, w_id FROM liefert)
SELECT h.h_id, h.h_name, h.h_ortschaft, COUNT(*) AS l_anzahl_waren
FROM haendler h LEFT JOIN l ON h.h_id = l.h_id
GROUP BY h.h_id, h.h_name, h.h_ortschaft
ORDER BY h_id
;
-- ohne Subquery
SELECT h.h_id, h.h_name, h.h_ortschaft, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM haendler h
LEFT JOIN liefert l ON (h.h_id = l.h_id)
GROUP BY h.h_id, h.h_name, h.h_ortschaft
ORDER BY h_id
;
-- mit korrelierter Subquery in der SELECT-Klausel
SELECT h_id, h_name, h_ortschaft,
(SELECT COUNT(DISTINCT w_id)
FROM liefert l
WHERE l.h_id = h.h_id
) AS l_anzahl_waren
FROM haendler h
;
-- mit LATERAL-JOIN
SELECT h_id, h_name, h_ortschaft, l_anzahl_waren
FROM haendler h, -- CROSS JOIN
LATERAL (SELECT COUNT(DISTINCT w_id)
FROM liefert l
WHERE l.h_id = h.h_id
) AS l(l_anzahl_waren)
;
Temporäre View
-- Nicht-korrelierte Subqueries können in eine temporäre View ausgelagert werden.
-- Temporäre Views sind vor allem dann wichtig, wenn eine Subquery mehrfach
-- in eine komplexen Anfrage verwendet wird.
WITH anzahl_alle_waren(l_anzahl_alle_waren)
AS -- MATERIALIZED
-- Subquery wird ausgewertet und temporär gespeichert (ohne Indexe!)
(SELECT COUNT(w_id) FROM ware)
SELECT h_id,
(SELECT COUNT(DISTINCT w_id) -- korrelierte Subquery
FROM liefert l
WHERE l.h_id = h.h_id -- Zugriff auf h.h_id,
) AS h_anzahl_waren, -- außerhalb der Subquery definiert
l_anzahl_alle_waren
FROM haendler h,
anzahl_alle_waren a -- CROSS JOIN OK, da a nur ein Tupel enthält
;
WITH anzahl_alle_waren(l_anzahl_alle_waren)
AS NOT MATERIALIZED -- Subquery wird nicht ausgewertet, sondern in Query eingefügt
-- => evtl. mehrfache Auswertung, aber Optimierung und
-- Indexausnutzung
(SELECT COUNT(w_id) FROM ware)
SELECT h_id,
(SELECT COUNT(DISTINCT w_id) -- korrelierte Subquery
FROM liefert l
WHERE l.h_id = h.h_id -- Zugriff auf h.h_id,
) AS l_anzahl_waren, -- außerhalb der Subquery definiert
l_anzahl_alle_waren
FROM haendler h,
anzahl_alle_waren a -- CROSS JOIN OK, da a nur ein Tupel enthält
;
-- Diese Version (mit NOT MATERIALIZED) entspricht wird in folgende
-- Anfrage übersetzt:
SELECT h_id,
(SELECT COUNT(DISTINCT w_id) -- korrelierte Subquery
FROM liefert l
WHERE l.h_id = h.h_id -- Zugriff auf h.h_id,
) AS l_anzahl_waren, -- außerhalb der Subquery definiert
FROM haendler h,
(SELECT COUNT(w_id) FROM ware) a -- CROSS JOIN OK, da a nur ein Tupel enthält
;
Subqueries in WHERE- und HAVING-Klauseln
/**************************************************************************
* Das IN-Prädikat:
* Welcher Händler liefert den Artikel mit der Warennumer 3?
**************************************************************************/
-- als Semin-Join (DISTINCT ist notwendig!)
SELECT DISTINCT h.h_id, h.h_name
FROM haendler h, liefert l
WHERE h.h_id = l.h_id AND l.w_id = 3
;
SELECT DISTINCT h.h_id, h.h_name
FROM haendler h JOIN liefert l USING (h_id)
WHERE l.w_id = 3
;
-- mit nicht-korrelierter Subquery (beste Lösung)
SELECT h.h_id, h.h_name
FROM haendler h
WHERE h.h_id IN
(SELECT l.h_id
FROM liefert l
WHERE l.w_id = 3
)
;
-- weniger performant, da korrelierte Subquery
SELECT h.h_id, h.h_name
FROM haendler h
WHERE 3 IN
(SELECT l.w_id
FROM liefert l
WHERE l.h_id = h.h_id
)
;
-- auch nicht besser
SELECT h.h_id, h.h_name
FROM haendler h
WHERE EXISTS
(SELECT *
FROM liefert l
WHERE l.h_id = h.h_id AND l.w_id = 3
)
;
Weitere Beispiel für das IN-Prädikat
/***************************************************************************
* explizite Aufzählung einer Menge
****************************************************************************/
SELECT *
FROM ware
WHERE w_typ IN ('CPU', 'RAM', 'Sonstiges')
;
SELECT *
FROM ware
WHERE w_typ = 'CPU' OR w_typ = 'RAM' OR w_typ = 'Sonstiges'
;
/***************************************************************************
* Das In-Prädikat mit Tupeln.
****************************************************************************/
SELECT ROW ('Maier', 'Königsbrunn')
IN
(SELECT h_name, h_ortschaft FROM haendler) AS ergebnis
;
Falsche Duplikate
/***************************************************************************
* Falsche Duplikate vermeiden!
* Wie lauten die Namen der Händler, die CPUs liefern?
****************************************************************************/
-- zu viele Tupel (falsche Duplikate)
SELECT h_name
FROM haendler JOIN liefert USING (h_id)
JOIN ware USING (w_id)
WHERE w_typ = 'CPU'
;
-- zu wenige Tupel (richtige Duplikate fehlen)
SELECT DISTINCT h_name
FROM haendler JOIN liefert USING (h_id)
JOIN ware USING (w_id)
WHERE w_typ = 'CPU'
;
-- richtige Antwort (Duplikate, aber keine falschen Duplikate)
SELECT h_name
FROM haendler
WHERE h_id
IN
(SELECT h_id
FROM liefert l JOIN ware w ON l.w_id = w.w_id
WHERE w_typ = 'CPU'
)
;
-- Es funktioniert auch mit einem Join; aber ohne Subquery geht es
-- immer noch nicht.
SELECT h_name
FROM (SELECT DISTINCT h_name, h_id
FROM haendler JOIN liefert USING (h_id)
JOIN ware USING (w_id)
WHERE w_typ = 'CPU'
) AS dummy
;
-- Am elegantesten ist die Lösung mit dem
-- DISTINCT-ON-Operator, der bislang
-- allerdings nur von PostgreSQL angeboten wird.
SELECT DISTINCT ON (h_id) h_name
FROM haendler JOIN liefert USING (h_id)
JOIN ware USING (w_id)
WHERE w_typ = 'CPU'
;
Divisionsoperator: Beispiel
-- Ausgangslage
-- Tabelle r:
SELECT DISTINCT h_id, w_id
FROM liefert
;
-- Tabelle s:
SELECT w_id
FROM ware
;
-- a = {hid, wid}\{w_id} = {h_id}
-- Alle Händler, die Waren liefern:
-- πa(r)
SELECT DISTINCT h_id
FROM liefert
;
-- Was wäre, wenn jeder Händler alles liefern würde?
-- πa(r)✕s
SELECT h_id, w_id
FROM (SELECT DISTINCT h_id
FROM liefert
) AS alle_haendler
CROSS JOIN
ware
;
-- Welche Händler liefern welche Waren NICHT (EXCEPT)?
-- (πa(r)✕s)\r
SELECT h_id, w_id
FROM (SELECT DISTINCT h_id
FROM liefert
) AS alle_haendler
CROSS JOIN
ware
EXCEPT
SELECT h_id, w_id
FROM liefert
;
Divisionsoperator: Beispiel (Fortsetzung)
-- Welche Händler liefern nicht alle Waren?
-- πa((πa(r)✕s)\r)
SELECT
DISTINCT h_id
FROM (SELECT h_id, w_id
FROM (SELECT DISTINCT h_id
FROM liefert
) AS alle_haendler
CROSS JOIN
ware
EXCEPT
SELECT h_id, w_id
FROM liefert
) AS wer_liefert_was_nicht
;
-- Welche Händler liefern alle Waren?
-- r ÷ s = πa(r) \ πa((πa(r)✕s)\r)
SELECT DISTINCT h_id
FROM liefert
EXCEPT
SELECT DISTINCT h_id
FROM (SELECT h_id, w_id
FROM (SELECT DISTINCT h_id
FROM liefert
) AS alle_haendler
CROSS JOIN
ware
EXCEPT
SELECT h_id, w_id
FROM liefert
) AS wer_liefert_was_nicht
;
-- Oder (effizienter) mit zusätzlicher Benutzung der Händler-Tabelle.
-- Aber immer noch ineffizient wegen CROSS JOIN.
SELECT h_id
FROM haendler
EXCEPT
SELECT h_id
FROM (SELECT h_id, w_id
FROM (SELECT h_id
FROM haendler
) AS alle_haendler
CROSS JOIN
ware
EXCEPT
SELECT h_id, w_id
FROM liefert
) AS wer_liefert_was_nicht
;
Für-Alle-Anfragen: Ersatzlösung mit EXISTS
SELECT DISTINCT l1.h_id
FROM liefert l1
WHERE NOT EXISTS -- Es gibt keine
(SELECT w_id
FROM ware -- Ware,
EXCEPT -- die der Händler l1.h_id nicht liefert
SELECT l2.w_id
FROM liefert l2 -- Diese Waren liefert er
WHERE l2.h_id = l1.h_id -- korrelierte Subquery
)
;
-- Oder noch performanter (da keine Duplikate entfernt werden müssen) .
SELECT h.h_id
FROM haendler h
WHERE NOT EXISTS
(SELECT w_id
FROM ware
EXCEPT
SELECT l.w_id
FROM liefert l
WHERE l.h_id = h.h_id
)
;
-- Alternative mit zweimal NOT EXISTS (doppelte Korrelation: sehr schlecht!)
SELECT h.h_id
FROM haendler h
WHERE NOT EXISTS
(SELECT *
FROM ware w
WHERE NOT EXISTS
(SELECT *
FROM liefert l
WHERE l.h_id = h.h_id
AND l.w_id = w.w_id
)
)
;
Für-Alle-Anfragen: Ersatzlösung mit Zählen
-- Alternativen mit Zählen (auch sehr effizient)
SELECT l.h_id, COUNT(DISTINCT w_id) AS anzahl -- anzahl war nicht gefragt
FROM liefert l
GROUP BY l.h_id
HAVING COUNT(DISTINCT w_id)
= (SELECT COUNT(*) FROM ware)
;
SELECT h_id
FROM (SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert l
GROUP BY h_id
) l
WHERE (SELECT COUNT(*) FROM ware) = l_anzahl_waren
;
SELECT h_id
FROM (SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert l
GROUP BY h_id
) l, -- CROSS JOIN: Kein Problem, da w nur ein Tupel enthält
(SELECT COUNT(*) AS w_anzahl_alle_waren
FROM ware
) w
WHERE l_anzahl_waren = w_anzahl_alle_waren
;
Min-Max-Anfragen
-- Alternativen mit Zählen (auch sehr effizient)
SELECT l.h_id, COUNT(DISTINCT w_id) AS anzahl -- anzahl war nicht gefragt
FROM liefert l
GROUP BY l.h_id
HAVING COUNT(DISTINCT w_id)
= (SELECT COUNT(*) FROM ware)
;
SELECT h_id
FROM (SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert l
GROUP BY h_id
) l
WHERE (SELECT COUNT(*) FROM ware) = l_anzahl_waren
;
SELECT h_id
FROM (SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert l
GROUP BY h_id
) l, -- CROSS JOIN: Kein Problem, da w nur ein Tupel enthält
(SELECT COUNT(*) AS w_anzahl_alle_waren
FROM ware
) w
WHERE l_anzahl_waren = w_anzahl_alle_waren
;
Echt kleiner an Stelle von kleiner gleich
/***************************************************************************
* Welche Waren liefert Maier aus Königsbrunn ECHT billiger
* als seine Kollegen?
* Beachten Sie, die Eieruhr entfällt, da Händler 2 sie zum selben Preis
* liefert wie Maier aus Königsbrunn.
*
* Den Speicherriegel (RAM) liefert nur Maier. Daher ist er auch
* billiger als seine Kollegen.
***************************************************************************/
SELECT wm.w_id, wm.w_typ, wm.w_bezeichnung, lm.l_preis, lm.l_lieferzeit
FROM haendler hm
JOIN liefert lm USING (h_id)
JOIN ware wm USING (w_id)
WHERE h_name = 'Maier' AND h_ortschaft = 'Königsbrunn' AND
l_preis < ALL (SELECT l_preis
FROM liefert la
WHERE la.w_id = lm.w_id AND la.h_id <> lm.h_id
)
;
Verwendung von MIN ist problematisch
-- Bei folgender Lösung wird der Speicherriegel nicht angezeigt,
-- da dieser Artikel nur von Maier aus Königsbrunn geliefert wird.
-- Daher kann für die anderen Händler kein Minimum ermittelt werden.
SELECT wm.w_id, wm.w_typ, wm.w_bezeichnung, lm.l_preis, lm.l_lieferzeit
FROM haendler hm
JOIN liefert lm USING (h_id)
JOIN ware wm USING (w_id)
WHERE h_name = 'Maier' AND h_ortschaft = 'Königsbrunn' AND
l_preis < (SELECT MIN(l_preis)
FROM liefert la
WHERE la.w_id = lm.w_id AND la.h_id <> lm.h_id
)
;
-- Workaround 1: Verwende < ALL anstelle von < (SELELCT MIN(...) ...)
-- (siehe oben)
-- Workaround 2: explizite Überprüfung,
-- ob andere Händler diese Ware liefern (hässlich)
SELECT wm.w_id, wm.w_typ, wm.w_bezeichnung, lm.l_preis, lm.l_lieferzeit
FROM haendler hm
JOIN liefert lm USING (h_id)
JOIN ware wm USING (w_id)
WHERE h_name = 'Maier' AND h_ortschaft = 'Königsbrunn' AND
(l_preis < (SELECT MIN(l_preis)
FROM liefert la
WHERE la.w_id = lm.w_id AND la.h_id <> lm.h_id
)
OR
(SELECT COUNT(*)
FROM liefert la
WHERE la.w_id = lm.w_id AND la.h_id <> lm.h_id
)
= 0
)
;
-- Workaround 3: einen künstlichen sündhaft teuren Preis einführen
SELECT wm.w_id, wm.w_typ, wm.w_bezeichnung, lm.l_preis, lm.l_lieferzeit
FROM haendler hm
JOIN liefert lm USING (h_id)
JOIN ware wm USING (w_id)
WHERE h_name = 'Maier' AND h_ortschaft = 'Königsbrunn' AND
l_preis < (SELECT MIN(l_preis)
FROM (SELECT h_id, w_id, l_preis FROM liefert
UNION
SELECT -1, lm.w_id, 999999.99 -- -1 sehr teurer Dummy-Händler
) la
WHERE la.w_id = lm.w_id AND la.h_id <> lm.h_id
)
;
Weitere Beispiele
SELECT w_id
FROM liefert lm
WHERE h_id = 1 AND
l_preis <= ALL (SELECT l_preis
FROM liefert la
WHERE la.w_id = lm.w_id
)
;
SELECT w_id
FROM liefert lm
WHERE h_id = 1 AND
l_preis = (SELECT MIN(l_preis)
FROM liefert la
WHERE la.w_id = lm.w_id
)
;
SELECT w_id
FROM liefert lm,
LATERAL (SELECT MIN(l_preis) AS min_preis
FROM liefert la
WHERE la.w_id = lm.w_id
) dummy
WHERE h_id = 1 AND l_preis = min_preis
;
SELECT w_id
FROM liefert lm
JOIN LATERAL (SELECT MIN(l_preis) AS min_preis
FROM liefert la
WHERE la.w_id = lm.w_id
) dummy
ON (l_preis = min_preis)
WHERE h_id = 1
;
SELECT w_id -- Alle Waren von Maier
FROM liefert
WHERE h_id = 1
EXCEPT -- mit Ausnahme derer,
SELECT w_id -- die andere billiger liefern
FROM liefert lm
WHERE h_id = 1 AND
EXISTS
(SELECT *
FROM liefert la
WHERE la.w_id = lm.w_id AND
la.l_preis < lm.l_preis
)
;
-- Kürzer mit NOT EXISTS
SELECT w_id
FROM liefert lm
WHERE h_id = 1 AND
NOT EXISTS
(SELECT *
FROM liefert la
WHERE la.w_id = lm.w_id AND
la.l_preis < lm.l_preis
)
;
Die meisten …
-- Wie viele unterschiedliche Waren liefern die einzelnen Händler?
SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert
GROUP BY h_id
;
-- Wer liefert die meisten unterschiedlichen Waren?
SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert
GROUP BY h_id
HAVING COUNT(DISTINCT w_id) =
(SELECT MAX(anzahl)
FROM (SELECT COUNT(DISTINCT w_id) AS anzahl
FROM liefert
GROUP BY h_id
) dummy
)
;
-- Dasselbe noch einmal DRY!
WITH anzahl_waren_je_haendler(h_id, l_anzahl_waren)
AS
(SELECT h_id, COUNT(DISTINCT w_id)
FROM liefert
GROUP BY h_id
)
SELECT h_id, l_anzahl_waren
FROM anzahl_waren_je_haendler
WHERE l_anzahl_waren =
(SELECT MAX(l_anzahl_waren)
FROM anzahl_waren_je_haendler
)
;
Geschachtelte Aggregation mit Windowing
-- Maximale Anzahl der Waren, die irgendein Händler liefert.
SELECT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
; -- Beachten Sie: Alle Werte sind mit Sicherheit gleich.
SELECT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
FETCH FIRST 1 ROW ONLY -- OK, da alle Werte mit Sicherheit gleich sind wg. OVER ()
;
SELECT DISTINCT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
;
-- Damit kann man die Frage auch beantworten.
SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert
GROUP BY h_id
HAVING COUNT(DISTINCT w_id) =
(SELECT DISTINCT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
)
;
-- Ohne DISTINCT, mit =SOME
SELECT h_id, COUNT(DISTINCT w_id) AS l_anzahl_waren
FROM liefert
GROUP BY h_id
HAVING COUNT(DISTINCT w_id) =SOME
(SELECT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
)
;
-- Ohne DISTINCT, mit =SOME und View
SELECT h_id, l_anzahl_waren
FROM anzahl_waren_je_haendler
WHERE l_anzahl_waren =SOME
(SELECT MAX(COUNT(DISTINCT w_id)) OVER () AS anzahl
FROM liefert
GROUP BY h_id
)
;