UNION

Nina123456

Neuer Benutzer
Beiträge
3
Hallo
smile.gif


In meiner Datenbank geht es um Schiffe und Schleusungen m:n. Ich muss mit einem SQL Editor folgende Abfrage für die angelegte Datenbank formulieren:

Listen Sie alle Schiffe mit Name und Art auf und ermitteln Sie jeweils die Anzahl an verschiedenen Schleusen, die sie bisher durchfahren haben ( als Ergebnisspalte "AnzahlSchleusungen"). Verwenden Sie die GROUP BY Funktion, um die Ergebnisse bezogen auf das Schiff ( Name ) auszugeben. Bei Schiffen, die noch keine Schleuse durchfahren haben, soll die Anzahl an durchfahrenen Schleusen mit einer Null versehen werden ( UNION Operator).

Nicht besonders schwer, wenn denn der letzte Satz nicht wäre...habe jetzt schon so viel über diesen UNION Operator gelesen aber es wird leider nichts...
hier ist mein Vorschlag, der jedoch nicht funktioniert
zuerst die Tabellen mit den entsprechenden Attributen

Schiff

schf_id
schf_name
schf_art

Schleuse
schl_id
schl_name

durchfaehrt
df_id
df_schf_id
df_schl_id

SELECT schf_name, schf_art, COUNT( schl_id ) AS AnzahlSchleusen
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id LIKE df_schf_id AND df_schl_id LIKE schl_id)
HAVING AnzahlSchleusen IS NOT NULL
UNION
SELECT schf_name, schf_art, COUNT( schl_id ) AS AnzahlSchleusen
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id LIKE df_schf_id AND df_schl_id LIKE schl_id )
HAVING AnzahlSchleusen IS NULL
GROUP BY schf_name, schf_art;

wenn ich die Zeilen ab UNION weglasse, dann gibt er mir nur die Schiffe mit vorhandenen Schleusenvorgängen an, jedoch nicht die Schiffe, die noch nicht durch Schleusen gefahren sind....füge ich diese aber ein, dann funktioniert es nicht mehr
Ich meine auch gelesen zu haben, dass die count funktion die Ergebnisse mit 0 nicht anzeigt


Vielen Dank schonmal
smile.gif
)))

lg Nina
 
Werbung:
Hallo
smile.gif


In meiner Datenbank geht es um Schiffe und Schleusungen m:n. Ich muss mit einem SQL Editor folgende Abfrage für die angelegte Datenbank formulieren:

lg Nina

Warum so umständlich?

Code:
test=*# select * from schiff;
 id |   name
----+----------
  1 | schiff 1
  2 | schiff 2
  3 | schiff 3
(3 rows)

Time: 0,232 ms
test=*# select * from schleuse ;
 id |    name
----+------------
  1 | schleuse 1
  2 | schleuse 2
(2 rows)

Time: 0,164 ms
test=*# select * from durchfahrt ;
 schiff | schleuse
--------+----------
      1 |        1
      1 |        2
      2 |        2
(3 rows)

Time: 0,157 ms
test=*# select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name;
   name   | count
----------+-------
 schiff 1 |     2
 schiff 3 |     0
 schiff 2 |     1
(3 rows)
 
SELECT schf_name, schf_art, COUNT( schl_id ) AS AnzahlSchleusen
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id LIKE df_schf_id AND df_schl_id LIKE schl_id)
HAVING AnzahlSchleusen IS NOT NULL
UNION
SELECT schf_name, schf_art, COUNT( schl_id ) AS AnzahlSchleusen
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id LIKE df_schf_id AND df_schl_id LIKE schl_id )
HAVING AnzahlSchleusen IS NULL
GROUP BY schf_name, schf_art;


lg Nina

  • warum arbeitest Du mit LIKE? Das ist absolut uncool hier und kann Dir erheblich das Resultat der Abfrage fälschen
  • vermutlich sind Deine ID-Spalten Text oder sowas, auch uncool
  • Du joins die Tabelle schleuse, die aber in Resultat gar keine Rolle spielt, unsinnig
 
Hallo :) erstmal danke für die schnellen Antworten,
die ABfrage ist eine Vorgabe, wir müssen also auf jeden fall den UNION Operator verwenden und auf keinen Fall JOIN Ausdrücke
Mein neuer Vorschlag:
SELECT schf_name, schf_kategorie, COUNT( df_schl_id ) AS [AnzahlSchleusen]
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id = df_schf_id AND df_schl_id = schl_id)

GROUP BY schf_name, schf_kategorie
[[[[hier gibt er nur die Schiffe raus, die bereits durch eine schleuse gefahren sind und mit UNION sollte er dann die restlichen Schiffe anzeigen, die eben keine schleusung hatten]]]]

UNION
SELECTschf_name, schf_kategorie, COUNT( df_schl_id ) AS [AnzahlSchleusen]
FROM Schiff, Schleuse, durchfaehrt
WHERE ( AnzahlSchleusen IS NULL AND schf_id = df_schf_id AND df_schl_id = schl_id)
GROUP BY schf_name, schf_kategorie;
 
SELECT schf_name, schf_kategorie, COUNT( df_schl_id ) AS [AnzahlSchleusen]
FROM Schiff, Schleuse, durchfaehrt
WHERE ( schf_id = df_schf_id AND df_schl_id = schl_id AND schf_id IN( SELECT df_schf_id FROM durchfaehrt))
GROUP BY schf_name, schf_kategorie
UNION
SELECT schf_name, schf_kategorie, COUNT (df_schl_id) AS [AnzahlSchleusen]
FROM Schiff, Schleuse, durchfaehrt
WHERE ( df_schl_id = schl_id AND schf_id = df_schf_id AND schf_id IN ( SELECT df_schf_id FROM durchfaehrt))
GROUP BY schf_name, schf_kategorie;

hier gibt er mir trotzdem nur die geschleusten Schiffe raus
 
Werbung:
Hallo :) erstmal danke für die schnellen Antworten,
die ABfrage ist eine Vorgabe, wir müssen also auf jeden fall den UNION Operator verwenden und auf keinen Fall JOIN Ausdrücke

Die Forderung, keinen JOIN zu verwenden, ist Bullshit. Auch wenn Du die Syntax ohne expliziete JOINs nutzt verwendest Du impliziete Joins, die Datenbank macht in beiden Fällen exakt dasselbe.
JOIN verbindet 2 Tabellen nebeneinander über definierte Felder, UNION verkleistert 2 Tabellen untereinander. Um zu der Anzahl der Schleusungen zu kommen, mußt Du in jedem Falle also erst einmal 2 Tabellen via JOIN verbinden, auch wenn Du die Syntax ohne JOIN verwendest.

Wenn Du nun unbedingt noch UNION verwenden willst, dann mache einmal die Abfrage mit HAVING > 0 und einmal mit HAVING = 0:

Code:
test=*# select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name having count(d.*) > 0 UNION select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name having count(d.*) = 0;
   name   | count
----------+-------
 schiff 3 |     0
 schiff 1 |     2
 schiff 2 |     1
(3 rows)

Das ist natürlich auch Bullshit, weil es einfacher ginge, das Explain zeigt es:

Code:
test=*# explain select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name having count(d.*) > 0 UNION select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name having count(d.*) = 0;
                                        QUERY PLAN
-------------------------------------------------------------------------------------------
 HashAggregate  (cost=240.10..244.10 rows=400 width=64)
   ->  Append  (cost=114.55..238.10 rows=400 width=64)
         ->  HashAggregate  (cost=114.55..117.05 rows=200 width=64)
               Filter: (count(d.*) > 0)
               ->  Hash Right Join  (cost=37.68..98.50 rows=2140 width=64)
                     Hash Cond: (d.schiff = a.id)
                     ->  Seq Scan on durchfahrt d  (cost=0.00..31.40 rows=2140 width=36)
                     ->  Hash  (cost=22.30..22.30 rows=1230 width=36)
                           ->  Seq Scan on schiff a  (cost=0.00..22.30 rows=1230 width=36)
         ->  HashAggregate  (cost=114.55..117.05 rows=200 width=64)
               Filter: (count(d.*) = 0)
               ->  Hash Right Join  (cost=37.68..98.50 rows=2140 width=64)
                     Hash Cond: (d.schiff = a.id)
                     ->  Seq Scan on durchfahrt d  (cost=0.00..31.40 rows=2140 width=36)
                     ->  Hash  (cost=22.30..22.30 rows=1230 width=36)
                           ->  Seq Scan on schiff a  (cost=0.00..22.30 rows=1230 width=36)
(16 rows)

Meine erste Lösung:

Code:
test=*# explain select a.name, count(d.*) from schiff a left join durchfahrt d on a.id=d.schiff group by a.name ;
                                  QUERY PLAN
-------------------------------------------------------------------------------
 HashAggregate  (cost=109.20..111.20 rows=200 width=64)
   ->  Hash Right Join  (cost=37.68..98.50 rows=2140 width=64)
         Hash Cond: (d.schiff = a.id)
         ->  Seq Scan on durchfahrt d  (cost=0.00..31.40 rows=2140 width=36)
         ->  Hash  (cost=22.30..22.30 rows=1230 width=36)
               ->  Seq Scan on schiff a  (cost=0.00..22.30 rows=1230 width=36)
(6 rows)

Merke: man kann IMMER etwas komplizierter machen als notwendig. Muß man aber nicht.

Andreas
 
Zurück
Oben