Zur Navigation

JOIN über mehrere Tabellen

1 AndiN

Hallo Jörg,
ich bin (leider mal wieder) am verzweifeln was ein JOIN über mehrere Tabellen angeht.

Ich habe folgende Tabellen:

NAME (Feld1,Feld2,...)

domizile (ID,Katalogtitel)
objekte (ID,DomizilID,ObjektName)
objektattribute_A (ObjektID,AttributID)
objektattribute_B (ObjektID,AttributID)
objektattribute_C (ObjektID,AttributID)

Die Hierarchie ist: Domizil => Objekt => Attribute

Ziel ist es sämtliche Objekte (!) mit ihrem dazugehörigen Domizil und ihren Attributen zu bekommen. Also quasi dann so:

array(ObjektID,ObjektName,DomizilID,Katalogtitel,AttributID1,AttributID2,AttributID3);

also z.B.

array
(
array(0,Objekt0,0,Domizil0,12,3,7);
array(1,Objekt1,0,Domizil0,7,123,5);
array(2,Objekt2,1,Domizil1,6,98,31);
);

Mal von der Ausgabe als Array abgesehen versuche ich das wie folgt:

SELECT
	domizile.ID DomizilID,
	domizile.Katalogtitel,
	objekte.ID ObjektID,
	objekte.ObjektName,
	objektattribute_A.AttributID AttributID1,
	objektattribute_B.AttributID AttributID2,
	objektattribute_C.AttributID AttributID3,
FROM domizile
	LEFT JOIN objekte ON objekte.DomizilID = domizile.ID
	LEFT JOIN objektattribute_A ON objektattribute_A.ObjektID = objekte.ID
	LEFT JOIN objektattribute_B ON objektattribute_B.ObjektID = objekte.ID
	LEFT JOIN objektattribute_C ON objektattribute_C.ObjektID = objekte.ID
	$connect = @mysql_query($query) or die(mysql_error());
	$val = mysql_fetch_row($connect);
	echo count($val);
	while($val)
	{
		echo $Domizil_ID = $val[0];
		....
	}

Das echo count($val) ergibt 19 Einträge aus obwohl nur 4 Domizile mit insgesammt 8 Objekten und 60 Attributen in der DB sind. Meiner Meinung nach sollte er also hier "8 Einträge" finden.

Wenn ich mir die $Domizil_ID ausgeben lasse sehe ich, dass er hier über hundert Schleifen-Druchläufe macht. Es kommt dann auch die PHP-Meldung "Fatal error: Maximum execution time of 30 seconds exceeded in..." bzw. dass ihm der Speicher ausgeht.

Frage1:
Wieso produziere ich hier offenbar eine Endlosschleife bzw. eine große Kombinationenanzahl?

Frage2:
Wie bekomme ich die korrekte Anzahl der Objekte geliefert?

12.04.2012 13:16

2 Jörg Kruse

$connect = @mysql_query($query) or die(mysql_error());

$connect ist eine etwas unpassende Bezeichnung, weil hier nicht die Verbindungskennung sondern eine Ergebnismenge zurückgegeben wird - $result wäre hier angebrachter

	$val = mysql_fetch_row($connect);

Damit holst du nur eine Zeile aus der Ergebnismenge

while($val)

Da gibt es keine Abbruchbedingung, deswegen die Endloschleife

Ich würde mysql_fetch_array() in der Schleife verwenden:

	while($val = mysql_fetch_array($result))
	{
		echo $Domizil_ID = $val[0];
		....
	}

Die zurückgegebenen Datensätze vorab zählen kannst du mit mysql_num_rows()

12.04.2012 13:40 | geändert: 12.04.2012 13:42

3 AndiN

Danke für die Hinweise!

mysql_num_rows() ergibt, dass ich 1613 Einträge auslese.
Ich habe das Gefühl, dass er in der JOIN-Abfrage jeden Eintrag mit jedem Eintrag kombiniert.

Eigentlich sollte die Abfrage lauten:

Hole mir alle Einträge aus den Tabellen die folgende Vorraussetzungen erfüllen:

domizile.objektID = objektattribute_A.ObjektID = objektattribute_B.ObjektID = objektattribute_C.ObjektID = objekte.ID;

12.04.2012 14:00

4 Jörg Kruse

Ich habe das Gefühl, dass er in der JOIN-Abfrage jeden Eintrag mit jedem Eintrag kombiniert.

Wie sehen denn die Ergebnisse konkret aus? (mit "LIMIT 50" kannst du die Ausgabe ja entsprechend begrenzen)

Wenn du die Objekte und nicht die Domizile abfragen möchtest, würde ich den JOIN anders anordnen:

FROM objekte
	LEFT JOIN domizile ON objekte.DomizilID = domizile.ID

12.04.2012 14:14

5 AndiN

So wie es aussieht macht er Folgendes:

14x die 0
529x die 4
1x die 1
1x die 3
1008x die 4
1x die 0


Die 0,1,3 und die 4 sind alles IDs von Domizilen.
Ich habe schon alle möglichen Kombinationen versucht wie er auf die jeweiligen ID-Anzahlen kommen könnte (Domizile x Objekte x Attribute, etc.) steige da jedoch nicht dahinter.

12.04.2012 15:04 | geändert: 12.04.2012 15:04

6 Jörg Kruse

Kommen denn die IDs der Objekte mehrfach vor? dann könnte ein DISTINCTROW nach dem SELECT helfen.

Falls das nicht hilft, würde ich es noch mit "INNER JOIN" statt mit "LEFT JOIN" versuchen. Das sortiert Datensätze aus, die nicht in allen Tabellen vorkommen.

12.04.2012 15:17 | geändert: 12.04.2012 15:19

7 AndiN

Die IDs der Objekte kommen nicht mehrfach vor.
Die IDs der Domizile kommen auch nicht mehrfach vor.
Jedoch gibt es die DomizilID 0 und auch die ObjektID 0.

Durch DISTINCTROW bekomme ich nun nicht mehr 1613 EInträge ausgegeben, sondern "nur" noch 1241

14x die 0
504x die 4
1x die 1
1x die 3
720x die 4
1x die 0

D.h. das Domizil mit der ID 4 verursacht jetzt weniger Ausgaben.

Bei INNER JOIN bekomme ich 0 EInträge ausgegeben (was ja eigentlich logisch ist, da kein Datensatz mehrfach vorkommt. Lediglich die ObjektID stellt die Verbindung zu allen Tabellen dahr. Lediglich die Tabelle domizile ist mit der Tabelle objekte mit der domizilID verbunden).

12.04.2012 15:32

8 Jörg Kruse

Die IDs der Objekte kommen nicht mehrfach vor.

In der Ergebnismenge? demnach müsste es dort 1241 verschiedene Objekt-IDs geben.

Durch DISTINCTROW bekomme ich nun nicht mehr 1613 EInträge ausgegeben, sondern "nur" noch 1241

Meine Empfehlung in Beitrag 4 hast du dabei auch berücksichtigt?

Bei INNER JOIN bekomme ich 0 EInträge ausgegeben (was ja eigentlich logisch ist, da kein Datensatz mehrfach vorkommt. Lediglich die ObjektID stellt die Verbindung zu allen Tabellen dahr. Lediglich die Tabelle domizile ist mit der Tabelle objekte mit der domizilID verbunden).

Da habe ich mich wohl missverständlich ausgedrückt. Bei einem LEFT JOIN werden in dem vorliegenden Fall auch Objekte ohne Attribute zurückgegeben. Bei einem INNER JOIN nur Objekte, zu denen jeweils mindestens ein Attribut zugeordnet ist. Wenn hierbei nichts zurückgegeben wird, liegt das vermutlich daran, dass es kein Objekt gibt, welches in jeder Attributtabelle mit seiner ID (als Fremdschlüssel) vertreten ist

Voraussetzung auch hier die Reihenfolge der JOINs wie in Beitrag 4 empfohlen

12.04.2012 16:01 | geändert: 12.04.2012 16:07

9 Jörg Kruse

Zitat von Jörg
Kommen denn die IDs der Objekte mehrfach vor? dann könnte ein DISTINCTROW nach dem SELECT helfen.

Sorry, da hatte ich vergessen, dass ein DISTINCTROW oft erst zusammen mit einem "GROUP BY" Statement Sinn macht.

SELECT DISTINCTROW
	domizile.ID DomizilID,
	/* etc. */
FROM objekte
	LEFT JOIN domizile ON objekte.DomizilID = domizile.ID
	/* etc. */
GROUP BY objekte.ID

12.04.2012 18:58

10 AndiN

Der Zusatz "GROUP BY objekte.ID" war des Rätsels Lösung !!! Danke !


Wein oder Schokolade? :)

13.04.2012 10:40