snorri: SQL: "Liste aller Veranstaltungen samt nächstem Termin"?

Hi alle,

ich bastle gerade an einem komplexen SQL-Statement herum und komme auf keinen grünen Zweig; vielleicht kann mir ja hier jemand einen Tipp geben ...

Gesetzt den Fall, ich will das Programm eines Kinos ausgeben, und zwar als Liste aller aktuellen Filme, jeweils mit dem nächsten Vorstellungstermin. Ich habe also zwei Tabellen:

film (id, name, beschreibung ...)
vorstellung (id, id_film, beginn, preis, saal, ...)

Typischerweise wird es ja zu jedem Film mehrere Vorstellungen in der zweiten Tabelle geben. Mein Ansatz:

SELECT film.*,
    vorstellung.*,
    film.id AS fid
FROM film, vorstellung
WHERE
    (film.id = vorstellung.id_film) AND
    (vorstellung.beginn =
        SELECT MIN(beginn)
        FROM vorstellung
        WHERE
            (id_film = fid) AND
            (beginn > NOW())
    )

Ich hole mir also die Zeit der nächsten Vorstellung aus einem Subquery und schränke meinen Hauptquery auf Datensätze ein, die dieses Datum haben.

Anscheinend habe ich mir das aber zu einfach vorgestellt, denn das funktioniert nicht. Ich erhalte zwar keine Fehlermeldung, aber auch keine Ergebnisse. Wenn ich die Bedingung (id_film = fid) aus dem Subquery nehme, dann kommt etwas -- aber natürlich nicht das richtige. Muss ich noch etwas zusätzlich unternehmen, um das Feld des Hauptqueries im Subquery verwenden zu können, oder liegt mein Verständnisfehler an anderer Stelle?

Danke für alle sachdienlichen Hinweise!

-- snorri

  1. film (id, name, beschreibung ...)
    vorstellung (id, id_film, beginn, preis, saal, ...)

    Ich würde das mit einem Join lösen.

    1. Ich würde das mit einem Join lösen.

      @Thorsten:
      Was für ne tolle Idee!!!!. Es ist bereits mit einem Join gelöst, wenn auch nicht in/mit expliziter JOIN Notation sondern from tabelle1, tabelle2, ....

      Ich würde das vielleicht besser so schreiben:

        
      SELECT outerFilm.*,  
          outerVorstellung.*  
          -- film.id AS fid   -- das kann weg, du hast ja film.id schon mittels outerFilm.*  
      FROM film outerFilm  
      INNER JOIN vorstellung outerVorstellung  
        ON outerFilm.id = outerVorstellung.id_film  
      WHERE    vorstellung.beginn =  
              (SELECT MIN(innerVorstellung.beginn)  
              FROM vorstellung innerVorstellung  
              WHERE  innerVorstellung.id_film = outerVorstellung.id_film -- rückreferenz zur korrelation  
                 AND innerVorstellung.beginn > NOW())  
      
      

      Imho, hat snorri etwas komisch die Abfragen miteinander verbunden (korreliert), evt. die Klammern falsch gesetzt?

      Hinweise an snorri:

      • nutze bitte explizite JOIN Syntax, liest sich besser und lässt ggf das DBMS besser optimieren
      • verzichte bitte auf * um Spalten zu selektieren, benenne stattdessen die auszugebenden Spalten explizit  (das Thema ist hier im Forum oft genug diskutiert worden)

      Grüsse, Frank

      1. Hallo Frank,

        puh, in die Syntax muss ich mich erst einmal einarbeiten, so tief bin ich in SQL noch nicht eingestiegen. Danke!

        Zur *-Angabe: Die Film-Datenbank war nur ein Beispiel. Aber auch in der realen Anwendung werde ich wohl nicht anders können, denn die Liste der einzelnen Felder wäre ellenlang. Mir ist aber klar, dass es vom Sicherheitsaspekt besser wäre.

        -- snorri

        1. 'nabend!

          puh, in die Syntax muss ich mich erst einmal einarbeiten, so tief bin ich in SQL noch nicht eingestiegen. Danke!

          Wieso, ist doch nix bahnbrechend kompliziertes dabei
          (1) SELECT für die Ausgabe
          (2) JOIN um eine weitere Tabelle anzuk
          (3) SELECT welches einen Skalarwert für den Vergleich zurückgibt

          (1) ist trivial, du gibst das an Feldern/Spalten an, was du ausgeben/selektieren willst
          (2) ist ne andere bessere Schreibweise für FROM tab1, tab2 WHERE tab1.x = tab2.x; WHERE wird zu ON .... daran gewöhnt man sich schnell
          (3) ist allein einfach erstmal GIB MIR DAS KLEINSTE DATUM VON ALLEN DATENSÄTZEN DEREN DATUM > JETZT IST
          da kommt dann nur die Verknüpfung mit einem Feld vom umgebenden SELECT dazu, fertig

          Zur *-Angabe: Die Film-Datenbank war nur ein Beispiel. Aber auch in der realen Anwendung werde ich wohl nicht anders können, denn die Liste der einzelnen Felder wäre ellenlang.

          Fiele Velder = sehr wahrscheinlich schlechtes (und damit dringend verbesserungswürdiges) Datenbankdesign.

          Mir ist aber klar, dass es vom Sicherheitsaspekt besser wäre.

          Vom Sicherheitsaspekt her? Wow, wie bitte? Das erläutere mal bitte.

          Ciao, Frank

  2. yo,

    immer gut zu wissen, welche dbms und welche version du benutzt. was dein problem angeht, so hat dich frank schon darauf hingewiesen, besser explizite joins zu benutzen. auch die verwendung von * bei der ausgabe der spalten kann kritisch sein.

    aber mein tipp an dich ist, warum eine korrelierte unterabfrage bentuzen, wenn du einfach über das datum in der veranstaltungstabelle gehen kannst ?

    SELECT f.id.....
    FROM film f
    INNER JOIN vorstellung v ON v.id_film = f.id
    WHERE v.begin >= NOW()
    ;

    Ilja