WHERE oder INNER JOIN

dth33

Grünschnabel
Hallo,

ich habe drei Tabellen, und zwar deliveries, tours und deliveries_tours.

1) Die Tabelle deliveries besteht aus id, market_id, name, vorname usw.

2) Die Tabelle tours hat natürlich ebenfalls eine id und vor allem ein tourdatum

3) Verknüpft werden beide mit deliveries_tours. Diese Tabelle hat nur zwei Felder, nämlich delivery_id und tour_id.

Nun möchte ich z.B. alle Touren mit tours.tourdatum = '2009-10-19' und deliveries.market_id = 1200 haben. Dazu ist mir bisher nur folgende Lösung eingefallen:

Code:
select * 
from deliveries_tours
inner join tours on tours.id = deliveries_tours.tour_id and tours.tourdatum = '2009-10-19'
inner join deliveries on deliveries.id = deliveries_tours.delivery_id and deliveries.market_id = 1200;

Frage an die Experten: Ist die Query überhaupt richtig? Jedenfalls bekomme ich brauchbare Ergebnisse. Kann man da was verbessern? Geht das auch über WHERE?

Ich danke im Voraus.

Steffen
 
Die Bedinung im ON-Teil geht schon. Aber siehe dieses Zitat aus der MySQL-Anleitung
Im ON-Teil sollten keine Bedingungen vorhanden sein, die zur Beschränkung der Datensätze in der Ergebnismenge verwendet werden; geben Sie solche Bedingungen besser in der WHERE-Klausel an. Es gibt aber Ausnahmen zu dieser Regel.

Du solltst es also über WHERE schreiben.

Da du je ein funktionierendes Query hast, hier einfach noch einige Formatierungstipps
1) Schreibe alle SQL-Wörter gross.
2) Spare nicht mit neuen Zeilen und Tabulatoren. Ein Query lässt sich gut formatiert sehr leicht lesen, auch wenns 10 man Verschachtelt ist.
3) Verwende Aliasse für die Tabellen. Es wird gleich viel Kürzer und einfacher lesbar
4) Sei vorsicht mit dem * bei Abfragen über mehrere Tabellen. Dein Query hat mehrere Spalten mit demselben Spaltennamen (zb: id). Wenn du auf das Resultat zugreiffen willst, bekommst du ev. deswegen eine Fehlermeldung
5) reduziere die Ausgabe bei SELECT auf die Felder die du wirklich brauchst. Nimm nicht unnötig Balast mit.

Code:
SELECT
    dt.tour_id,
    dt.delivery_id, 
    t.name,
    t.strecke,
    d.feld1
FROM
    deliveries_tours AS dt
    INNER JOIN tours AS t
        ON t.id = dt.tour_id 
    INNER JOIN deliveries AS d 
        ON d.id = dt.delivery_id
WHERE 
    t.tourdatum = '2009-10-19'
    AND d.market_id = 1200;

Wenn die Tabellen tour und delivery riesig sind, lohnt es sich ggf. ein Subquery zu verwenden um die Datenmenge vor dem JOIN zu reduzieren.

Code:
SELECT
    dt.tour_id,
    dt.delivery_id, 
    t.name,
    t.strecke,
    d.feld1
FROM
    deliveries_tours AS dt
    INNER JOIN (SELECT 
                    * 
                FROM
                    delivery 
                WHERE 
                    market_id = 1200
                ) AS d
        ON d.id = dt.delivery_id
    INNER JOIN (SELECT 
                    *
                FROM
                    tours
                WHERE
                    tourdatum = '2009-10-19'
                ) AS t 
        ON t.id = dt.tour_id;

Ein INNER JOIN lösst sich aber auch über WHERE lösen
Code:
SELECT
    dt.tour_id,
    dt.delivery_id, 
    t.name,
    t.strecke,
    d.feld1
FROM
    deliveries_tours AS dt,
    delivery AS d,
    deliveries AS t
WHERE
    t.id = dt.tour_id
    AND d.id = dt.delivery_id
    AND d.market_id = 1200
    AND t.tourdatum = '2009-10-19';

Diese Dinge lassen sich je anch Ansprüchen, Datenmengen etc. zu Sinnvollem und weniger Sinnvollem kombinieren.

In der Tabelle deliveries_tours sollte wenn möglich ein eineutiger Index über die 2 ID-Felder gelegt sein.
 
Hallo,

danke für die kompetente und ausführliche Antwort. Ich werde das mal am Wochenende ausprobieren und auf jeden Fall ein Feedback abgeben.

Grüße Steffen
 
Hallo,

also ich habe die Vorschläge mal ausgetestet.

Einziges Manko ist, dass er jetzt zu jeder Lieferung auch die Tour anzeigt. Was ich jetzt habe ist ein Array mit xy Einträgen (Tour = 15, Lieferung = 12; Tour = 15, Lieferung = 77) usw. Die Tour_id wiederholt sich also. Das ist ok, aber........

Kann man die Ergebnismenge auch so reduzieren, dass er mir die Tour_id nur einmal aus der Datenbank holt? Nehmen wir mal an, es gibt: 20 Lieferungen mit market_id = 1200 für die Tour mit Datum '2009-10-20'. Die Tour hat die ID = 99. Sobald auch nur eine einzige Lieferung die Bedingung erfüllt, soll mir die Query als Ergebnis TourId = 99 geben. Die restlichen 19 Lieferungen kann ich doch auch später auslesen. Das erscheint jetzt vielleicht etwas kleinkariert, aber wenn ich mir überlege, dass später mal die Touren über einen Zeitraum xy ausgelesen werden sollen, kann man so vielleicht etwas schneller ans Ergebnis kommen.

Mit Group By bekomme ich sowas hin. Ist das richtig?

Code:
		return $this->query("
			SELECT
			    t.id, 
			    t.tourdatum,
			    t.fahrer_id,
			    t.beifahrer_id,
			    t.car_id,
			    d.id,
			    d.belegnummer,
			    c.id,
			    c.kennzeichen,
			    e.id,
			    e.name
			FROM
			    deliveries_tours AS dt,
			    deliveries AS d,
			    tours AS t
			LEFT JOIN
			    cars AS c ON c.id = t.car_id
			LEFT JOIN
				employees AS e ON e.id = t.fahrer_id
			WHERE
			    t.id = dt.tour_id
			    AND d.id = dt.delivery_id
			    AND d.market_id IN ($markets)
			    AND t.tourdatum = '$tourdate'
			GROUP BY t.id;    
	    ");

Hat jemand ne bessere Lösung?

Danke
 
Ich würd es irgendwie so probieren:

zuerst die Tour-ID bestimmen, die betroffen ist. Dazu auch die erste gefundene delevery-id.
Das ergibt in etwa das folgende Subquery:
Code:
SELECT DISTINCT
            FIRST(d.id) AS first_delivery_id, 
            t.id 
        FROM
            deliveries_tours AS dt,
            deliveries AS d,
            tours AS t
        WHERE
            t.id = dt.tour_id
            AND d.id = dt.delivery_id
            AND d.market_id IN ($markets)
            AND t.tourdatum = '$tourdate'

Nun hängen wir noch tours und delivery an um die Daten zu den IDs auszugeben. Natürlich auch die 2 LEFT JOINS:

Code:
SELECT
    t.id, 
    t.tourdatum,
    t.fahrer_id,
    t.beifahrer_id,
    t.car_id,
    d.id,
    d.belegnummer,
    c.id,
    c.kennzeichen,
    e.id,
    e.name
FROM
    (   SELECT DISTINCT
            FIRST(d.id) AS first_delivery_id, 
            t.id 
        FROM
            deliveries_tours AS dt,
            deliveries AS d,
            tours AS t
        WHERE
            t.id = dt.tour_id
            AND d.id = dt.delivery_id
            AND d.market_id IN ($markets)
            AND t.tourdatum = '$tourdate'
    ) AS selectedTours, 
    deliveries AS d,
    tours AS t
    LEFT JOIN cars AS c 
        ON c.id = t.car_id
    LEFT JOIN employees AS e 
        ON e.id = t.fahrer_id
WHERE
    t.id = selectedTours.id
    AND d.id = selectedTours.first_delivery_id
 
Zurück