Tobias Hahner: schwierige Abfrage MySQL

Hallihallo!

Ich stehe gerade ein Wenig auf dem Schlauch mit einer Datenbankabfrage in MySQL 5.0.67 auf MacOSX 10.6.

Und zwar habe ich folgende Tabellen:

vehicles

  • id int 11 auto_increment PRIMARY KEY
  • name varchar(20)
  • ... weitere unwichtige eigenschaften ...

trips

  • id int 11 auto_increment PRIMARY KEY
  • starttime datetime
  • destinationid id unsigned (ID eines in einer anderen Tabelle gehaltenen Ortes)

vehicles2trips

  • id int 11 auto_increment PRIMARY KEY
  • vehicleid int 10 unsigned
  • tripid int 10 unsigned
    (Mir ist klar, dass ich hier eigentlich keine Spalte "id" brauche, aber ich denke nicht, dass diese Spalte Schaden anrichtet ;) )

Es werden also in der Tabelle "vehicles" verschiedene Fahrzeuge (Fuhrpark, also Zugmaschinen und Anhänger) verwaltet, und in der Tabelle "trips" werden geplante Fahrten dieser Fahrzeuge eingetragen.
Da zu einem Trip theoretisch ein Zugfahrzeug mit einem Anhänger gehören können, gibt es die Zuordnungstabelle "vehicles2trips"…

Zum tieferen Verständnis:
Ein "Trip" zeichnet sich, wie in der Datenstruktur schon ersichtlich ist, nur durch 2 Eigenschaften aus: den Zeitpunkt der Abfahrt, und den Bestimmungsort. Da es hier nur um näherungsweise Planung geht, ist dieses vereinfachte Modell aus der Praxis heraus vollkommen ausreichend…

Nun zu meinem eigentlichen Problem:
Um den Aufenthaltsort eines Fahrzeugs (definiert durch seine ID) zum einem bestimmten Zeitpunkt "giventime" zu erfahren, möchte ich nun Folgendes von der Datenbank:

"Gib mir den letzten 'trip', dessen starttime kleiner ist als "giventime", und der in 'vehicles2trips' mit "given_vehicleid" verknüpft ist."

Mein erster Ansatz war:

  • gib mir alle Tripids zur gegebenen vehicleid
  • gehe alle entsprechenden Trips in einer Schleife durch und merke dir den richtigen.
    Dieser Ansatz war: 1) in PHP schnell gebaut, 2) suboptimal, weil zigtausend Querys an MySQL gehen...

Der nächste Ansatz war:

  
SELECT  
   vehicles2trips.tripid as tripid,  
   trips.destinationid as destinationid,    //(DIESEN WERT SUCHE ICH HAUPTSÄCHLICH!)  
FROM  
   vehicles2trips  
WHERE  
   vehicles2trips.vehicleid = /*givenvehicleid*/  
LEFT JOIN  
   trips  
ON  
   trips.id = vehicles2trips.tripid  

Damit (die Abfrage ist jetzt aus dem Kopf, weil das Original viel grösser, aber prinzipiell genauso ist) bekomme ich zumindest ALLE trips, die zu dem gesuchten vehicle "gehören".
Ich müsste aber in einer Schleife nun per PHP denjenigen Datensatz raussuchen, der die Bedingung "der letzte vor dem gegebenen Datum" erfüllt.
Machbar, aber doof…

Frage ist nun:
Wie kann ich den (/das? Aufklärung erbeten ;) ) Query so erweitern, dass MySQL mit seinen Datums-und Zeitfunktionen die Filterarbeit übernimmt und mir von vornherein nur DEN EINEN Datensatz zurückgibt, den ich suche?
Schliesslich möchte ich mit PHP diesen Datensatz nur auswerten, es ist nicht PHPs Job, sich den Datensatz erst zu suchen. Das ist Job von MySQL…

Ich bin für alle Lösungsansätze, auch Hinweise auf suboptimale Datenhaltung, dankbar...

Beste Grüße,
Tobias Hahner

  1. moin,

    vehicles2trips

    • id int 11 auto_increment PRIMARY KEY
    • vehicleid int 10 unsigned
    • tripid int 10 unsigned
      (Mir ist klar, dass ich hier eigentlich keine Spalte "id" brauche, aber ich denke nicht, dass diese Spalte Schaden anrichtet ;) )

    also eine typische m:n beziehung zwischen den entitätstypen vehicles und trips. und für mich ist es nicht unsinning, noch mal einen zusätzlichen PK wer in der beziehungstabelle zu führen. du solltest aber die beiden fremdschlüssel NOT NULL setzen und einen UNIQUE Key über beide spalten bilden.

    "Gib mir den letzten 'trip', dessen starttime kleiner ist als "giventime", und der in 'vehicles2trips' mit "given_vehicleid" verknüpft ist."

    zum einen könnte man sich ja auch vorstellen, dass ein fahrzeug nicht auf einem "trip" ist, sondenr im fuhrpark steht. dazu brauchst du dann aber einen zweiten zeitwert bis wann ein trip geht.

    Wie kann ich den (/das? Aufklärung erbeten ;) ) Query so erweitern, dass MySQL mit seinen Datums-und Zeitfunktionen die Filterarbeit übernimmt und mir von vornherein nur DEN EINEN Datensatz zurückgibt, den ich suche?

    mit einer korrelierten unterabfrage. aber noch ein hinweis, der OUTER JOIN ist falsch herum. du musst von vehicles ausgehen, schließlich kann es ja sein, dass das fahrzeug noch nie auf einem trip war. und dann wird es kniffelig, du musst die korrelierte unterabfrage auch noch in die ON klausel des OUTER JOINS einbauen, eben weil aus oben genannten bedinungen nicht jedes fahrzeug auch einen trip haben muss. allerdings würde ich mir vorher erst mal überlegen, ob du nicht neben der startzeit eines trips auch noch das ende festhälst.

    Ilja

    1. als nachtrag, du kannst auch ganz auf JOINS verzichten und in der FROM klausel nur die vehicles haben, deren ORT dann über eine korrelierte unterabfrage in der projektion angezeigt wird.

      Ilja

      1. Hallihallo!

        als nachtrag, du kannst auch ganz auf JOINS verzichten und in der FROM klausel nur die vehicles haben, deren ORT dann über eine korrelierte unterabfrage in der projektion angezeigt wird.

        Das ist jetzt ziemlich harter Tobak für mich, da ich *hüstel* noch nie mit "korellierten Unterabfragen" zu tun hatte.
        Ich weiss zwar im Prinzip, was sie machen, aber ich traue mir nicht zu, mal eben ein entsprechendes Subselect zusammenzubauen…

        Kannst Du mir sagen, wie so etwas in etwa aussehen müsste?
        Nein, ich möchte keine fertige Abfrage, mir reicht ja schon Pseudocode :)

        Beste Grüsse,
           Tobias Hahner

    2. Hallihallo!

      also eine typische m:n beziehung zwischen den entitätstypen vehicles und trips. und für mich ist es nicht unsinning, noch mal einen zusätzlichen PK wer in der beziehungstabelle zu führen. du solltest aber die beiden fremdschlüssel NOT NULL setzen und einen UNIQUE Key über beide spalten bilden.

      Ja, das ist auch schon so gelöst. Nur hatte ich das aus Gründen der Übersichtlichkeit hier nicht dargelegt.

      "Gib mir den letzten 'trip', dessen starttime kleiner ist als "giventime", und der in 'vehicles2trips' mit "given_vehicleid" verknüpft ist."

      zum einen könnte man sich ja auch vorstellen, dass ein fahrzeug nicht auf einem "trip" ist, sondenr im fuhrpark steht. dazu brauchst du dann aber einen zweiten zeitwert bis wann ein trip geht.

      Das kam mir auch schon in den Sinn. Das Problem mit "es gibt keine Trips" habe ich im Frontend folgendermassen gelöst:
      Da gelegentlich auch mal Fahrzeuge dazugemietet werden, wird auch die Verfügbarkeit des Fahrzeugs (availablefrom, availabletill) in der Tabelle "vehicles" festgehalten. (Da diese beiden Spalten Nichts mit dem eigentlichen Problem zu tun haben, habe ich sie im OP unterschlagen und unter "weitere unwichtige Eigenschaften" zusammengefasst... Da steht noch viel mehr drin, z.B. maximales Zuladegewicht, TüV- Intervall, Inspektionsintervall usw...)
      Wie dem auch sei, vom Frontend aus trage ich für jedes neue Fahrzeug auch einen Trip ein, und zwar mit date=availablefrom und destinationid=0 (Lager).

      Zudem geht es in meiner Abfrage ja genau darum, nicht einen Trip zu finden, der GENAU JETZT stattfindet, sondern den LETZTEN VOR einem gegebenen Datum.

      mit einer korrelierten unterabfrage. aber noch ein hinweis, der OUTER JOIN ist falsch herum. du musst von vehicles ausgehen, schließlich kann es ja sein, dass das fahrzeug noch nie auf einem trip war. und dann wird es kniffelig, du musst die korrelierte unterabfrage auch noch in die ON klausel des OUTER JOINS einbauen, eben weil aus oben genannten bedinungen nicht jedes fahrzeug auch einen trip haben muss.

      Wie gesagt: der Fall, dass es keinen Trip gibt, kann nicht auftreten: Jedes Fahrzeug hat mindestens einen Trip, und zwar hin zum Lager.

      allerdings würde ich mir vorher erst mal überlegen, ob du nicht neben der startzeit eines trips auch noch das ende festhälst.

      Das habe ich absichtlich nicht getan, und zwar aus folgendem Grund: Die Einträge in der Tabelle "trips" sind (ursprünglich) für die Zukunft gedacht. Sprich: Planung von noch anstehenden Touren. Die Erfahrung hat gezeigt, dass das tatsächliche Ende eines Trips so gut wie nie mit dem theoretischen Ende übereinstimmt.
      Deswegen wird diese Information schlicht ignoriert, und die Fahrzeuge werden auf der Planungsskizze einfach "gebeamt" [1] :) (und nein, es besteht kein Bedarf nach einer Routine, die aus der Erfahrung lernt und das Ende von geplanten Trips zuverlässiger ermittelt :) )

      Ilja

      Beste Grüsse,
         Tobias Hahner

      [1] beamen heisst hier: Fahrzeug fährt los, und ist gleichzeitig am Zielort

  2. Hi!

    "Gib mir den letzten 'trip', dessen starttime kleiner ist als "giventime", und der in 'vehicles2trips' mit "given_vehicleid" verknüpft ist."

    Weil es Ilja nicht erwähnt hat: Den Datensatz der kleiner aber am nächsten dran an giventime ist, bekomst du, indem du alle kleineren wählst, absteigend nach der Zeit sortierst und mit LIMIT das Ergebnis auf einen beschränkst.

    Lo!

    1. Hallihallo!

      Also mit so Etwas?

        
      SELECT  
         vehicles2trips.tripid as tripid,  
         trips.destinationid as destinationid,    //(DIESEN WERT SUCHE ICH HAUPTSÄCHLICH!)  
      FROM  
         vehicles2trips  
      WHERE  
         vehicles2trips.vehicleid = /*givenvehicleid*/  
      LEFT JOIN  
         trips  
      ON  
         trips.id = vehicles2trips.tripid  
      ORDER BY trips.starttime desc  
      LIMIT 1  
      
      

      Daran hatte ich auch schon gedacht, aber den Gedanken dann wieder verworfen.
      Nach meinem Verständnis wird mit dieser Abfrage nämlich erstmal die komplette Lösungsmenge "zusammengewurschtelt", dann sortiert, und schliesslich auf 1 Element beschnitten. Das hielt ich nicht für sonderlich geschickt…

      Oder unterliege ich da einem Denkfehler?

      Beste Grüße,
          Tobias Hahner

      1. Hallihallo!

        Da fällt mir grade ein: Dieses Select hätte mich wahnsinnig gemacht, da es mir immer den letzten Trip _überhaupt_ gibt, ich aber den letzten Trip vor giventime suche...

        Das bringt mich zu folgendem nächsten Versuch:

          
        SELECT  
           vehicles2trips.tripid as tripid,  
           trips.destinationid as destinationid,    //(DIESEN WERT SUCHE ICH HAUPTSÄCHLICH!)  
        FROM  
           vehicles2trips  
        WHERE  
           vehicles2trips.vehicleid = /*givenvehicleid*/  
        LEFT JOIN  
           trips  
        ON  
           (  
           	( trips.id = vehicles2trips.tripid )  
           	AND  
           	( trips.starttime < /* givenvehicleid */ )  
           )  
        ORDER BY trips.starttime desc  
        LIMIT 1  
        
        

        Wobei mir jetzt aber auffällt, dass ich nach einer Spalte sortiere, die evtl. NULL ist, weil sie die starttime- Bedingung nicht erfüllt…
        Ich glaube, ich geh erstmal eine rauchen, ich blockiere mich grade mental selbst *grmblfrx*

        Beste Grüsse,
            Tobias Hahner

        --
        Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
      2. Hi!

        Also mit so Etwas?

        Nicht ganz, du musst auch Ilja Hinweis auf die korrellierte Unterabfrage beachten. Ich warf nur einen Teilaspekt ein, der dir das neueste X liefert. Damit musst du nun den verknüpften Datensatz suchen gehen.

        Gruppiere über die Fahrzeuge, finde den neuesten Stand zu jedem (korrellierte Subquery), hol dir die Daten zu diesem. Vermutlich musst du das mit mehreren Subquerys lösen.

        Nach meinem Verständnis wird mit dieser Abfrage nämlich erstmal die komplette Lösungsmenge "zusammengewurschtelt", dann sortiert, und schliesslich auf 1 Element beschnitten. Das hielt ich nicht für sonderlich geschickt…

        In Daten zu suchen ist Aufgabe eines DBMS, daraufhin ist es ausgelegt. Gelegentlich helfen eingebaute Optimierungen. Dein Job ist es, Indexe passend zu erstellen, damit Sortierungen nicht jedes Mal ausgeführt werden müssen, sondern über einen Index ein schellerer Zugriff erfolgen kann. In einer sortierten Menge eine bestimmte Anzahl von Daten zu finden ist nicht mehr sehr aufwendig.

        Lo!

  3. da du wirklich nur diese ID brauchst, spricht nichts dagegen den ganzen JOIN Kram zu lassen und einfach so abzufragen:

    SELECT
    vehicles2trips.tripid as tripid,
    trips.destinationid as destinationid,    //(DIESEN WERT SUCHE ICH HAUPTSÄCHLICH!)
    FROM
    trips, vehicles2trips
    WHERE
    trips.id = vehicles2trips.tripid
    AND trips.starttime < /*giventime*/
    AND vehicles2trips.vehicleid = /*givenvehicleid*/
    ORDER BY trips.starttime DESC

    /* maybe */
    GROUP BY vehicles2trips.vehicleid
    /* OR */
    LIMIT 1

  4. Hallihallo!

    Nach den hier gegebenen Hinweisen und einer halben Stunde des Einlesens in Subselects (die gar nicht so schwer zu verstehen sind, wie ich dachte) habe ich eine Lösung gefunden, die ich hier natürlich mit allen teilen und zur eventuellen Diskussion stellen möchte:

      
    SELECT sub.tripid AS tripid, sub.starttime AS starttime, sub.destinationid AS destinationid  
    FROM (  
       /* subselect Anfang*/  
       SELECT  
          vehicles2trips.tripid AS tripid,  
          trips.starttime AS starttime,  
          trips.destinationid AS destinationid  
       FROM vehicles2trips  
       LEFT JOIN  
          trips  
       ON (  
          trips.id = vehicles2trips.tripid  
       )  
       WHERE vehicles2trips.vehicleid = /* given_vehicleid */  
       /* ende subselect */  
    ) AS sub  
    WHERE (  
       sub.starttime < "/* given_date */"  
    )  
    ORDER BY sub.starttime DESC  
    LIMIT 1  
      
    
    

    Eigentlich gar nicht so schwer gewesen, man muss sich nur gedanklich Stück für Stück näher an die gesuchte Lösungsmenge rantasten…

    Ungeachtet der Tatsache, dass Verbesserungsvorschläge für diesen SELECT gerne angenommen werden, möchte ich mich an dieser Stelle bei allen Helfenden herzlich bedanken!

    Beste Grüsse,
        Tobias Hahner

    --
    Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
    1. moin,

      [code lang=sql]
      SELECT sub.tripid AS tripid, sub.starttime AS starttime, sub.destinationid AS destinationid
      FROM (
         /* subselect Anfang*/
         SELECT
            vehicles2trips.tripid AS tripid,
            trips.starttime AS starttime,
            trips.destinationid AS destinationid
         FROM vehicles2trips
         LEFT JOIN
            trips
         ON (
            trips.id = vehicles2trips.tripid
         )
         WHERE vehicles2trips.vehicleid = /* given_vehicleid */
         /* ende subselect */
      ) AS sub
      WHERE (
         sub.starttime < "/* given_date */"
      )
      ORDER BY sub.starttime DESC
      LIMIT 1

      ich mag LIMIT nicht, das hat zwei gründe. zum einen ist es mysql spezifisch, läßt sich also nicht portieren. aber damit könnte ich leben. das andere argument ist, es besteht dabei immer eine latente gefahr, nicht alle datensätze zu erwischen. wenn man den nur wirklich einen datensatz haben will, dann mag das noch klappen, aber was wenn nicht und sich die werte in der order bei klausel gleichen ? besser ist es immer mit MAX und MIN zu arbeiten.

      ich will mich mal auf die anderen aussagen von dir bezüglich meiner post beziehen. zum einen sagst du, ein vehicle hat immer einen trip. wozu dann noch der OUTER JOIN ? zum anderen stellstdu das mit einem frontend sicher und das ist mal gefährlich. damit ist nicht sichergestellt, dass es solche fälle nicht gibt.

      antworte später vielleicht noch mal, muss in eine sitzung....

      Ilja

      1. Hallihallo!

        besser ist es immer mit MAX und MIN zu arbeiten.

        Den Satz musste ich erstmal sacken lassen, aber ich denke, ich weiss jetzt, was Du meinst...
        Morgen früh werde ich mich mal daran versuchen, den Hinweis umzusetzen.

        ich will mich mal auf die anderen aussagen von dir bezüglich meiner post beziehen. zum einen sagst du, ein vehicle hat immer einen trip. wozu dann noch der OUTER JOIN ?

        Hier muss ich jetzt nochmal nachhaken: ich benutze hier den Join, um die Querverbindung von
        Vehicles2trips zu vehicles herzustellen.
        Oder willst Du darauf hinaus, dass ein anderer Join sinnvoller wäre? Welcher wäre das?

        zum anderen stellstdu das mit einem frontend sicher und das ist mal gefährlich. damit ist nicht sichergestellt, dass es solche fälle nicht gibt.

        Da hast Du sicher recht, dass das nicht ganz optimal ist. Ich hatte auch Bedenken dabei, habe aber mein
        Gewissen damit beruhigt, dass das automatische Anlegen dieses "Initialtrips" wenigstens in der Kontrolle meines vehicle- Objekts bleibt. Auf eventuelle Fehler bin ich bzw. ist der Programmfluss vorbereitet und reagiert entsprechend.

        Ich denke fast, dass es übertrieben wäre, die Konsistenz der Daten auf Datenbakebene zu gewährleisten...  ich wüsste auch gar nicht, wie das bewerkstelligt werden kann...
        Aber wie immer, bin ich für Vorschläge, auch in Form von Links zu schlaumachenden Quellen, dankbar.

        Beste Grüsse,
            Tobias Hahner

        --
        Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
        1. moin,

          ich will mich mal auf die anderen aussagen von dir bezüglich meiner post beziehen. zum einen sagst du, ein vehicle hat immer einen trip. wozu dann noch der OUTER JOIN ?
          Hier muss ich jetzt nochmal nachhaken: ich benutze hier den Join, um die Querverbindung von
          Vehicles2trips zu vehicles herzustellen.
          Oder willst Du darauf hinaus, dass ein anderer Join sinnvoller wäre? Welcher wäre das?

          mal davon abgesehen, dass bei deiner lösung die unterabfrage unnötig ist (ich kann jedenfalls keinen sinn darin sehen), einen OUTER JOIN verwendet man dann, wenn man in der einen tabelle daten hat, die in einer anderen tabelle nicht mindestens ein entsprechender gegenpart besitzen, man aber auf jeden fall den datensatz haben will. und wenn ich dich richtig verstanden habe, dann hat ein vevicle immer einen trip und somit auch eine eintrag in der beziehungstabelle. das würden einen OUTER JOIN überflüssig machen. zum anderen solltest du dir für den fall gedanken machen, wenn starttime = given_date ist.

          Ich denke fast, dass es übertrieben wäre, die Konsistenz der Daten auf Datenbakebene zu gewährleisten...

          hmm, ein satz, der in meinen augen schmertzt ;-)

          Ilja

          1. Hallihallo!

            moin,

            ich will mich mal auf die anderen aussagen von dir bezüglich meiner post beziehen. zum einen sagst du, ein vehicle hat immer einen trip. wozu dann noch der OUTER JOIN ?
            Hier muss ich jetzt nochmal nachhaken: ich benutze hier den Join, um die Querverbindung von
            Vehicles2trips zu vehicles herzustellen.
            Oder willst Du darauf hinaus, dass ein anderer Join sinnvoller wäre? Welcher wäre das?

            mal davon abgesehen, dass bei deiner lösung die unterabfrage unnötig ist (ich kann jedenfalls keinen sinn darin sehen),

            Ich habe es versucht, ohne eine Unterabfrage zu schaffen. Der Versuch war gescheitert, woraufhin ich diesen Thread gestartet habe. Wenn ich mich recht erinnere, hast Du mir den Tip mit der "korellierten Unterabfrage" gegeben, oder vertue ich mich da?

            einen OUTER JOIN verwendet man dann, wenn man in der einen tabelle daten hat, die in einer anderen tabelle nicht mindestens ein entsprechender gegenpart besitzen, man aber auf jeden fall den datensatz haben will. und wenn ich dich richtig verstanden habe, dann hat ein vevicle immer einen trip und somit auch eine eintrag in der beziehungstabelle. das würden einen OUTER JOIN überflüssig machen.

            OK, ich gebe zu, dass ich in Sachen SQL nicht besonders bewandert bin. Bisher kam ich mit meinem jetzigen Wissen immer zurecht, aber es wird Zeit, mein Wissen den Anforderungen entsprechend zu erweitern... Ich kenne den Link auf den Artikel "Einführung in Joins", und werde ihn mal genauestens studieren. Wenn ich was nicht verstehe, melde ich mich hier wieder.

            zum anderen solltest du dir für den fall gedanken machen, wenn starttime = given_date ist.

            Habe ich mittlerweile getan. Ich habe insgeheim meine Abfrage auf "WHERE starttime <= giventime " geändert, was auch das gewünschte Ergebnis liefert.

            Ich denke fast, dass es übertrieben wäre, die Konsistenz der Daten auf Datenbakebene zu gewährleisten...

            hmm, ein satz, der in meinen augen schmertzt ;-)

            Auch wenn es mir peinlich ist, fragen zu müssen: schmerzt er Dir in den Augen, weil meine Unwissenheit zum Himmel schreit? (Falls ja: den Satz "Ich wüsste auch nicht, wie das geht" hättest Du dann ruhig stehenlassen und meine Wissenslücken dichten können, wenigstens mit einem Hinweis).
            Oder schmerzt er Dir in den Augen, weil Du im Gegensatz zu mir weisst, dass das sowieso nicht geht? (Wobei ich denke, dass es eigentlich gehen sollte…)
            Bitte, klär mich auf, erhelle meine geistige Umnachtung :)

            Beste Grüsse,
                Tobias Hahner

            --
            Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
            1. Ich habe es versucht, ohne eine Unterabfrage zu schaffen. Der Versuch war gescheitert, woraufhin ich diesen Thread gestartet habe. Wenn ich mich recht erinnere, hast Du mir den Tip mit der "korellierten Unterabfrage" gegeben, oder vertue ich mich da?

              eventuell?

              1. Hallihallo!

                eventuell?

                Den von Dir hier verlinkten Post habe ich im Laufe der Diskussion (Nichts für ungut) bewusst ausser acht gelassen, und zwar aus folgenden Gründen:

                1. Natürlich habe ich das Statement in PHPMyAdmin ausprobiert. Es hagelte mir Syntaxfehler, die ich beim besten Willen nicht nachvollziehen konnte. (ich war aber auch in einem ziemlich frustrierten Zustand, muss ich zugeben)
                2. Du schriebst "auf den ganzen JOIN-Kram verzichten". Aber selbst ich mit meinem begrenzten SQL- Wissen konnte erkennen, dass Dein Statement "joint"…

                Und da ich, wie schon gesagt, zu dem Zeitpunkt ziemlich frustriert war, aber nicht meinen Unmut hier im Forum breittreten wollte, habe ich dieses Dein Posting einfach ignoriert…[1]

                Beste Grüsse,
                    Tobias Hahner

                [1] Hatte aber wirklich Nichts mit Deinem Posting an sich, oder gar mit Dir zu tun. Ich kam mit Deinem Hinweis eben einfach nicht weiter/klar…

                --
                Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
                1. Hallihallo!

                  Moin,

                  Den von Dir hier verlinkten Post habe ich im Laufe der Diskussion (Nichts für ungut) bewusst ausser acht gelassen, und zwar aus folgenden Gründen:

                  1. Natürlich habe ich das Statement in PHPMyAdmin ausprobiert. Es hagelte mir Syntaxfehler, die ich beim besten Willen nicht nachvollziehen konnte. (ich war aber auch in einem ziemlich frustrierten Zustand, muss ich zugeben)

                  Da ich keine DB mit einer solchen Struktur habe, hab ich das ding mal eben so aus dem Kopf geschrieben. Es ging mir auch darum etwas zu verdeutlichen.

                  Ich sehe zwar auf den ersten Blick kein Fehler, jedoch wird er nicht sehr kompliziert sein.

                  1. Du schriebst "auf den ganzen JOIN-Kram verzichten". Aber selbst ich mit meinem begrenzten SQL- Wissen konnte erkennen, dass Dein Statement "joint"…

                  Es verknüpft 2 Tabellen, wenn du das meinst... das muss es auch, sonst würde es nicht funktionieren. Mit "JOIN-Kram verzichten", war das explizite JOIN Statement mit ON Klausel gemeint.
                  Jedoch werden hier beide Tabellen anders verknüpft. Wenn hier eine Tabelle leer (bzw Bedingung nicht erfüllt) ist, so erscheint auch kein Result (kann positiv und negativ sein, aber in diesem Fall erscheint es mir sinnvoll).

                  Diese Art Tabellen zu verknüpfen war die erste Art die ich gelernt hab, vermutlich halte ich sie deshalb für die einfachste.

                  Der Punkt ist: Man gibt beide Tabellen als Quelle an und verknüpft sie beim WHERE direkt "a.ID = b.ID". Danach kann man mit beiden Tabellen arbeiten als währe es eine, das ist bei allen anderen Methoden nicht so einfach.

                  Und da ich, wie schon gesagt, zu dem Zeitpunkt ziemlich frustriert war, aber nicht meinen Unmut hier im Forum breittreten wollte, habe ich dieses Dein Posting einfach ignoriert…[1]

                  Gut, wenn ich das gewusst hätte, hätte ich es natürlich nicht nochmal erwähnt ;)

                  Beste Grüsse,
                      Tobias Hahner

                  mfg Pryos

                  [1] Hatte aber wirklich Nichts mit Deinem Posting an sich, oder gar mit Dir zu tun. Ich kam mit Deinem Hinweis eben einfach nicht weiter/klar…

                  kommt vor

                  1. moin,

                    Ich sehe zwar auf den ersten Blick kein Fehler, jedoch wird er nicht sehr kompliziert sein.

                    ein komma in der projektion ist am ende zuviel und die group by klausel würde so nicht funktionieren. zum einen nicht, weil sie vor dem ORDER BY stehen muss, zum anderen nicht weil du spalten ausgeben würdest, über die nicht gruppiert wurde oder die keine aggregat-funktion benutzen. mysql mag dort zwar keine fehlermeldung geben, aber inhaltlich würde es nicht mehr paassen.

                    Es verknüpft 2 Tabellen, wenn du das meinst... das muss es auch, sonst würde es nicht funktionieren. Mit "JOIN-Kram verzichten", war das explizite JOIN Statement mit ON Klausel gemeint.

                    das ist so nicht richtig. ein join hat nichts damit zu tun, ob ich die explizite schreibweise benutze oder nicht. du joinst in gleicher weiser, machst es aber unübersicherlicher.

                    Jedoch werden hier beide Tabellen anders verknüpft. Wenn hier eine Tabelle leer (bzw Bedingung nicht erfüllt) ist, so erscheint auch kein Result (kann positiv und negativ sein, aber in diesem Fall erscheint es mir sinnvoll).

                    auch das hat nichts mit deiner impliziten schreibweise zu tun. die tabellen werden bei der impliziten oder expliziten schreibeweise ganz genauso verknüpft, zumindestens was den INNER JOIN angeht. letzlich ist es sogar so, dass das dbms im ausführungsplan die bedinungen nach der ON klausel wieder in die WHERE klausel zieht.

                    Diese Art Tabellen zu verknüpfen war die erste Art die ich gelernt hab, vermutlich halte ich sie deshalb für die einfachste.

                    das habe ich auch, zummal oracle die explizite schreibweise erst ab 9i unterstützt hat. es hat aber meiner meinung nach nur nachteile und keine vorteile, die implizite schreibweise zu benutzen. es macht es einfach unübersichtlicher.

                    Der Punkt ist: Man gibt beide Tabellen als Quelle an und verknüpft sie beim WHERE direkt "a.ID = b.ID". Danach kann man mit beiden Tabellen arbeiten als währe es eine, das ist bei allen anderen Methoden nicht so einfach.

                    auch diese aussage ist nicht richtig, wobei ich der fairness fragen sollte, was genau es den bei der expliziten schreibweise schwieriger macht ?

                    Ilja

            2. moin,

              Ich habe es versucht, ohne eine Unterabfrage zu schaffen. Der Versuch war gescheitert, woraufhin ich diesen Thread gestartet habe. Wenn ich mich recht erinnere, hast Du mir den Tip mit der "korellierten Unterabfrage" gegeben, oder vertue ich mich da?

              das mit den korrelierten unterabfragen bezieht sich auf die verwendung von MIN / MAX. wenn du wirklch nur von einem fahrzeug den entprechenden einen datensatz haben willst, geht es auch mit einem INNER JOIN und LIMIT. wie das ohne unterabfrage geht, wurde dir schon gezeigt, wobei ich immer die implizite JOIN schreibeweise verwenden würde. aber wie gesagt, ich mag die LIMIT notation nicht, sie führst zwar zu einer schnellen lösung, aber verleitet auch sehr schnell. und wennn du deine abfrage ein wenig erweiterst und nicht nur von einem fahrzeug den letzten tripp haben willst, sondern von allen fahrzeugennach vor einem bestimmten zeitraum, dann solltest du erst recht auf den MIN / MAX weg umsteigen.

              Auch wenn es mir peinlich ist, fragen zu müssen: schmerzt er Dir in den Augen, weil meine Unwissenheit zum Himmel schreit?

              nein, das hat weniger mit dir zu tun. alle lernen dazu, auch IT profis, dafür ist die thematik insgesamt viel zu komplex. es hat mehr mit mir zu tun, ich bin ein spezialist, sprich mein wissen ist bezüglich der IT sehr begrentzt und zwar auf die welt der datenbanken. da gibt es viele, die haben ein wesentlich umfangreicheres wissen aus bereichen system-administration, programmierung, web, etc. ich lebe halt nur in meiner kleinen welt und versuche sie mit armen und beinen zu verteidigen. und da tut eben solch ein satz weh.;-)

              Ilja

              1. das mit den korrelierten unterabfragen bezieht sich auf die verwendung von MIN / MAX. wenn du wirklch nur von einem fahrzeug den entprechenden einen datensatz haben willst, geht es auch mit einem INNER JOIN und LIMIT. wie das ohne unterabfrage geht, wurde dir schon gezeigt, wobei ich immer die implizite JOIN schreibeweise verwenden würde. aber wie gesagt, ich mag die LIMIT notation nicht, sie führst zwar zu einer schnellen lösung, aber verleitet auch sehr schnell. und wennn du deine abfrage ein wenig erweiterst und nicht nur von einem fahrzeug den letzten tripp haben willst, sondern von allen fahrzeugennach vor einem bestimmten zeitraum, dann solltest du erst recht auf den MIN / MAX weg umsteigen.

                Bei deinem Beispiel würde ich dann zu GROUP BY greifen.
                Jedoch versteh ich dein MIN / MAX hinweis nicht, ich kenne diese Begriffe nur im Zusammenhang mit GROUP BY ... um den höchsten oder niedrigsten wert zu ermitteln bzw für die HAVING Bedingung.

                1. moin,

                  Bei deinem Beispiel würde ich dann zu GROUP BY greifen.
                  Jedoch versteh ich dein MIN / MAX hinweis nicht, ich kenne diese Begriffe nur im Zusammenhang mit GROUP BY ... um den höchsten oder niedrigsten wert zu ermitteln bzw für die HAVING Bedingung.

                  es gibt sicherlich immer viele wege, die nach rom führen. welcher machbar ist, hängt auch immer von dem report ab, den man haben will. bei GROUP BY kannst du neben den aggregat-funktionen nur die spalten ausgeben, über die du auch gruppiert hast, nicht mehr und nicht weniger. du willst aber manchmal noch mehr spalten ausgeben, kannst aber die gruppierung nicht verändern. dann kommt es oftmals zum einsatz von korrelierten unterabfragen.

                  MIN / MAX kommt hier mit ins spiel, weil er nach einen bestimmten maximal wert fragt, also in dem falle MAX. pseudo code könnte so aussehen:

                  verbinde die drei tabellen vehicles, vehicles2trips, und trips, zeige mir davon alle spalten.

                  SELECT *
                  FROM vehicles v
                  INNER JOIN vehicles2trips vt ON vt.vehicleid  = v.id
                  INNER JOIN trips t ON t.id = vt.tripid
                  ;

                  und von all den datensätze will ich nur die datensätze haben, mit einer bestimmten vehicle id und deren startdatum kleiner als ein bestimmer wert.

                  SELECT *
                  FROM vehicles v
                  INNER JOIN vehicles2trips vt ON vt.vehicleid  = v.id
                  INNER JOIN trips t ON t.id = vt.tripid
                  WHERE v.id = 15
                  AND t.starttime <= '31.12.2009'
                  ;

                  und von den datensätzen, die noch übrig bleiben, nur den mit dem höchsten startdatum.

                  SELECT *
                  FROM vehicles v
                  INNER JOIN vehicles2trips vt ON vt.vehicleid  = v.id
                  INNER JOIN trips t ON t.id = vt.tripid
                  WHERE v.id = 15
                  AND t.starttime <= '31.12.2009'
                  AND t.starttime = (SELECT MAX(t2.starttime )
                                     FROM vehicles2trips vt2
                                     INNER JOIN trips t2 ON t2.id = vt2.tripid
                                     WHERE vt2.vehicleid  = v.id
                                     AND t2.starttime <= t.starttime
                                    )
                  ;

                  schränke ich es nicht auf ein bestimmtes vehicle ein, kann ich mir das sogar für jedes farzeug zeigen lassen.

                  SELECT *
                  FROM vehicles v
                  INNER JOIN vehicles2trips vt ON vt.vehicleid  = v.id
                  INNER JOIN trips t ON t.id = vt.tripid
                  WHERE t.starttime <= '31.12.2009'
                  AND t.starttime = (SELECT MAX(t2.starttime )
                                     FROM vehicles2trips vt2
                                     INNER JOIN trips t2 ON t2.id = vt2.tripid
                                     WHERE vt2.vehicleid  = v.id
                                     AND t2.starttime <= t.starttime
                                    )
                  ;

                  Ilja

                  1. Hallihallo!

                    pseudo code könnte so aussehen:

                    […]

                    Ich danke Dir tausendfach, Ilja. Allein durch die 3 Beispiele habe ich mehr gelernt, als in der gesamten Diskussion vorher :) Es macht schon einen grossen Unterschied, ob man einmal eine schöne, elegante Lösung von einem Könner liest, oder stundenlang irgendwelche Anleitungen, wie es gehen KANN, ohne irgend einen Bezug zum Kernproblem…
                    Diese Anleitungen führen Einen nämlich dann auf meinen hölzernen, umständlichen Weg…
                    Also nochmals: Besten Dank :)

                    Beste Grüsse,
                        Tobias Hahner

                    --
                    Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
                  2. MIN / MAX kommt hier mit ins spiel, weil er nach einen bestimmten maximal wert fragt, also in dem falle MAX. pseudo code könnte so aussehen:

                    Jetzt verstehe ich wie du das meinst.

                    Wirkt zwar für den Zweck übertrieben, aber ist deutlich erweiterbarer.

  5. Hallihallo!

    Nachdem meine ursprüngliche Frage nun geklärt ist (nochmals vielen Dank an alle Helfenden), habe ich nun eine weitere Frage.
    Ich beziehe mich damit auf https://forum.selfhtml.org/?t=194734&m=1302822:

    ein satz, der in meinen augen schmerzt

    Ich bin, wie wahrscheinlich mittlerweile Jeder weiss :), bestenfalls ein gehoben mittelmässiger Laie auf dem Gebiet SQL (meint: MySQL)
    Aber ich möchte mich verbessern!
    Zu diesem Zweck würde ich als nächsten Schritt gerne wissen:

    Wie stelle ich, alein mit Hausmitteln von MySQL, die referentielle Integrität zwischen verschiedenen Tabellen sicher?

    Ich stelle mir das in etwa so vor:

    MySQL bekommt den INSERT- Befehl für ein neues vehicle.
    Ich möchte mich darauf verlassen können, dass in diesem Fall automatisch ein neuer Datensatz in der Tabelle "trips" eingefügt wird (mit Standardwerten, versteht sich),
    und eine entsprechende Verknüpfung dieser beiden neuen Einträge in "vehicles2trips" erstellt wird.

    Ist da "Stored Procedures" das Schlagwort, über das ich mich schlau machen muss?

    Verzeiht bitte die möglicherweise für einen Könner blöd klingende Frage, aber ich möchte irgendwann vielleicht auch ein Könner sein :)

    Beste Grüsse,
        Tobias Hahner

    --
    Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)
    1. Hi!

      Wie stelle ich, alein mit Hausmitteln von MySQL, die referentielle Integrität zwischen verschiedenen Tabellen sicher?

      Das kann MySQL nur mit der InnoDB-Storage-Engine.

      MySQL bekommt den INSERT- Befehl für ein neues vehicle.
      Ich möchte mich darauf verlassen können, dass in diesem Fall automatisch ein neuer Datensatz in der Tabelle "trips" eingefügt wird (mit Standardwerten, versteht sich),
      und eine entsprechende Verknüpfung dieser beiden neuen Einträge in "vehicles2trips" erstellt wird.

      Das kannst du auch mit einem Trigger erreichen. MyISAM (die Standard-Storage-Engine) kann jedoch nicht allein mit einer Tabellenkonfiguration sicherstellen, dass bei Löschvorgängen referenzierte Datensätze berücksichtigt werden. Das kann jedoch InnoDB.

      Ist da "Stored Procedures" das Schlagwort, über das ich mich schlau machen muss?

      Ja, denn Trigger sind mit Stored Procedures irgendwie verwandt. Zumindest werden sie im gleichen Kapitel behandelt - Stored Programs and Views.

      Verzeiht bitte die möglicherweise für einen Könner blöd klingende Frage, aber ich möchte irgendwann vielleicht auch ein Könner sein :)

      Hey, hör auf, dich zu bemitleiden :-) Wichtiger als Entschuldigungen ist für uns™ als Antwortende, dass du Lernwillen zeigst. Das machst du. Alles bestens. Dass beim Lernen noch nicht alles perfekt sitzt ist völlig normal und verständlich. Lücken kann man ja durch Fragen und Antworten füllen.

      Lo!

      1. Hallihallo!

        Ist da "Stored Procedures" das Schlagwort, über das ich mich schlau machen muss?

        Ja, denn Trigger sind mit Stored Procedures irgendwie verwandt. Zumindest werden sie im gleichen Kapitel behandelt - Stored Programs and Views.

        Besten Dank für den Link. Ich werde mich da mal durchackern, und höchstwahrscheinlich wieder hier melden…

        Beste Grüsse,
            Tobias Hahner

        --
        Da hat mir das Schicksal mal wieder voll auf meine Lacklederstiefel gekotzt (Mad Jack, Zeichtrickpirat)