# MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhanden



## Mik3e (1. Juli 2005)

Abend!

Klingt etwas verwirrend der Thread, hier die Erklärung:

Es gibt zwei Tabellen, siehe ERD.

Nun möchte ich aus der Tabelle "tbl_saalplan_platz" alle Datensätze auslesen, die NICHT als Fremdschlüssel in der Tabelle "tbl_kategorie_join_platz" vorhanden sind.

*Beispiel (zur leichteren Verständlichkeit sind die Attribute vom ERD gekürzt):*

```
#tbl_saalplan_platz (pk_saalplan_platz_id, saalplan_platz_element_xbezeichnung)
1; testdatensatz 1
2; testdatensatz 2
3; testdatensatz 3

#tbl_kategorie_join_platz (fk_saalplan_platz_id)
1;
2;
```
Der SQL Query soll mir nun nur den dritten Datensatz der Tabelle "tbl_saalplan_platz" liefern. (3; testdatensatz 3). Ergibt sich daraus, da die beiden Primärschlüssel der Tabelle "tbl_saalplan_platz" in der Tabelle "tbl_kategorie_joing_platz" vorhanden sind.

Bin bisher leider nicht über den Stein der Weisen gestolpert in der Angelegenheit.
Danke im Voraus & LG
Mike


----------



## hpvw (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*


```
SELECT *
FROM tbl_saalplan_platz
LEFT JOIN tbl_kategorie_join_platz
ON (tbl_saalplan_platz.pk_saalplan_platz_id
  = tbl_kategorie_join_platz.fk_saalplan_platz_id)
WHERE 
tbl_kategorie_join_platz.fk_saalplan_platz_id IS NULL
```


----------



## Mik3e (1. Juli 2005)

Dankeschön.. wußte nicht, das IS NULL auch auf die Datensatzanzahl angewandt werden kann..
Andere Frage: Hast Du eigentlich auch noch ein Leben neben dem Forum   

THX & LG
Mike


----------



## hpvw (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*



			
				Mik3e hat gesagt.:
			
		

> Dankeschön.. wußte nicht, das IS NULL auch auf die Datensatzanzahl angewandt werden kann..


Wird es auch nicht. LEFT JOIN verknüpft, im Gegensatz zu JOIN, die Tabellen auf die Weise, dass die Datensätze aus der linken Tabelle mindestens einmal im Ergebnis auftauchen. Sollte es zu einem Datensatz aus der linken Tabelle keinen der ON-Bedingung entsprechenden Datensatz in der rechten Tabelle geben, werden die Felder der rechten Tabelle null gesetzt. Dies macht man sich bei dieser Abfrage zunutze. Es ist jedoch darauf zu achten, dass das Feld, welches man mit IS NULL vergleicht als NOT NULL definiert ist, da sonst auch Datensätze aufgenommen werden können, die eine Entsprechung in der der rechten Tabelle haben. Fremdschlüssel sind in der Regel NOT NULL.



			
				Mik3e hat gesagt.:
			
		

> Andere Frage: Hast Du eigentlich auch noch ein Leben neben dem Forum


Demnächst stehen Klausuren an. Tutorials.de ist als Ablenkung vom Lernen noch besser geeignet als "Zimmer aufräumen" und macht wesentlich mehr Spaß. Das Gewissen leidet allerdings.
Meine zweite Persönlichkeit führt übrigends ein Leben ohne Comupter 

Gruß hpvw


----------



## Mik3e (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*



			
				hpvw hat gesagt.:
			
		

> Meine zweite Persönlichkeit führt übrigends ein Leben ohne Comupter



Na dann bin ich ja froh, dass ich nicht der einzige schizophrene IT-Freak hier bin 

Das er beim LEFT JOIN im Gegensatz zum INNER JOIN alle Datensätze verwendet, war mir klar. Das er im Hintergrund die Werte auf NULL setzt, wußte ich nicht.

LG
Mike


----------



## Mik3e (1. Juli 2005)

Sorry, muss nochmal nerven..
Jetzt wirds etwas komplexer. Die Tabelle "tbl_kategorie_join_platz" ist (wie der Name verrät) eine assoziative Tabelle.

Sie verbindet die Datensätze der Tabelle "tbl_saalplan_platz" mit den Datensätzen der Tabelle "tbl_kategorie". Richtig, die Tabelle "tbl_kategorie" ist bei diesem ERD neu.

*Ich muss den Query nun dahin erweitern, dass er mir * 

```
1. alle Datensätze aus Tabelle "tbl_saalplan_platz" liefert
2. die NICHT in Tabelle "tbl_kategorie_join_platz" verknüpft sind, sofern der Fremdschlüssel in dieser Tabelle besagt, 
3. dass das Attribut "fk_vorstellung_id" in Tabelle "tbl_kategorie" einen bestimmten, eindeutigen Wert hat
```

Mein bisheriger (fehlgeschlagener Versuch) sieht so aus:

```
SELECT
t1.`pk_saalplan_platz_id` AS `pk_saalplan_platz_id`
FROM `tbl_saalplan_platz` AS t1
LEFT JOIN `tbl_kategorie_join_platz` AS t2 ON t1.`pk_saalplan_platz_id`=t2.`fk_saalplan_platz_id`
LEFT JOIN `tbl_kategorie` AS t3 ON t2.`fk_kategorie_id`=t3.`pk_kategorie_id`
WHERE t1.`fk_sektor_id`='<BESTIMMTER WERT>' 
AND (t2.`fk_saalplan_platz_id` IS NULL AND t3.`fk_vorstellung_id`='<BESTIMMTER WERT>')
```

Liegt meiner Meinung nach (wäre zumindest logisch) daran, dass die Regeln der WHERE-Clause erst auf die ausgelesenen Datensätze angewandt wird. Nachdem in Tabelle "tbl_kategorie_join_platz" aber möglicherweise kein einziger Datensatz vorhanden ist, liefert das SELECT auch keine Datensätze für die WHERE Clause.

Ist schon etwas komplexer.. Hoffe Du kannst mir damit auch helfen.
Viel Glück bei den Klausuren,
LG
Mike


----------



## Mik3e (1. Juli 2005)

Ich habe die Befürchtung, dass dieses Problem ohne SUBSELECT gar nicht zu lösen sein wird...


----------



## hpvw (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*



			
				Mik3e hat gesagt.:
			
		

> Liegt meiner Meinung nach (wäre zumindest logisch) daran, dass die Regeln der WHERE-Clause erst auf die ausgelesenen Datensätze angewandt wird. Nachdem in Tabelle "tbl_kategorie_join_platz" aber möglicherweise kein einziger Datensatz vorhanden ist, liefert das SELECT auch keine Datensätze für die WHERE Clause.


Das sehe ich auch so. Du benötigst irgendeine Bedingung, über die eine Beziehung zwischen dem Datensatz in der tbl_saalplan_platz-Tabelle und der tbl_kategorie-Tabelle hergestellt wird. Wenn eine solche Verbindung nicht existiert, sehe ich auch für ein Subquery schlechte Karten.

Wenn ich Dich richtig verstanden habe, bedeutet das ja: _Finde die Datensätze, die in keiner Beziehung zur anderen Tabelle stehen und dort einen bestimmten Wert haben._

Da ich nicht glaube, dass Du das so meinst, schau noch mal in Deine Tabellenstruktur und versuche die Bedingungen etwas klarer zu formulieren (bitte nicht in Code-Tags, da muss man so viel scrollen).



			
				Mik3e hat gesagt.:
			
		

> Viel Glück bei den Klausuren,


Danke.

Gruß hpvw


----------



## Mik3e (1. Juli 2005)

Hi!

Einfach gesagt brauche ich folgendes Resultat:
Ich brauche alle Plätze aus der Tabelle "tbl_saalplan_platz", die zu einem bestimmten Sektor gehören (Attribut: fk_sektor_id).
Alle Plätze, die mit einer Kategorie verknüpft sind (Tabelle "tbl_kategorie_join_platz"), sollen zusätzlich das Attribut "fk_kategorie_id" aus dieser Tabelle mitliefern, jene die nicht verknüpft sind sollen bei diesem Attribut NULL liefern.

Soweit kein Problem.
ABER:
Er darf mir den Wert für das Attribut "fk_kategorie_id" nur dann liefern, wenn die zugewiesene Kategorie zu einer bestimmten Vorstellung gehört (Tabelle "tbl_kategorie" -> Attribut "fk_vorstellung_id"). Ansonsten soll er ebenfalls NULL für das Attribut "fk_kategorie_id" liefern.
--------------------------------------------------------------------------

Ich experimentiere gerade mit UNION rum, scheint aber auch nicht wirklich zu klappen.
Eine unelegante Lösung wäre sicher, das Attribut "fk_vorstellung_id" einfach in der assoziativen Tabelle mitzuführen ("tbl_kategorie_join_platz"). Das wäre dann aber doch eine böse normalverletzung. Ich hab Dir zum Verständnis auch noch die Tabelle für die Vorstellungen ins ERD eingebaut.

Ich stehe echt an, wüßte nicht, wie ich das lösen könnte.. Da brauchts nen echten SQL Profi denke ich. Oder ich steh einfach nur auf einer verdammt fetten Leitung...

LG
Mike


----------



## hpvw (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Kann es sein, dass folgendes (oder ähnlich) in Deinem letzten Post nicht dargestellt ist?
Der Saalplanplatz ist über Sektor und der über Saalplan mit der Vorstellung verknüpft?


----------



## Mik3e (1. Juli 2005)

Ja, das ist richtig..
Im alten ERD gab es die Saalplan-Thematik noch nicht. Habe ich damals auch weggelassen um weniger Verwirrung zu stiften.

Dennoch stehe ich jetzt vor diesem (wirklich großem) Problem. Denn sollte das nicht zu managen sein, müsste ich das ERD nochmals ändern. Eine andere Form würde aber zu einer Normalverletzung (wie oben angegeben) führen.

Bist meine letzte Hoffnung


----------



## hpvw (1. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

So, ich habe mal einen Versuch gestartet. An den Fett markierten Stellen musst Du die Bedingungs-IDs einfügen:
	
	
	



```
SELECT
tbl_saalplan_platz.pk_saalplan_platz_id,
tbl_saalplan_platz.fk_sektor_id,
tbl_saalplan_platz.saalplan_platz_element_xpos,
tbl_saalplan_platz.saalplan_platz_element_ypos,
tbl_saalplan_platz.saalplan_platz_element_xbezeichnung,
tbl_saalplan_platz.saalplan_platz_element_ybezeichnung,
tbl_saalplan_platz.saalplan_ist_platz,
if(tbl_kategorie.fk_vorstellung_id=[dieVorstellungID],
  tbl_kategorie_join_platz.fk_kategorie_id,
  NULL) AS kategorie_id
FROM tbl_saalplan_platz
LEFT JOIN tbl_kategorie_join_platz
ON (tbl_saalplan_platz.pk_saalplan_platz_id
  =tbl_kategorie_join_platz.fk_saalplan_platz_id)
LEFT JOIN tbl_kategorie
ON (tbl_kategorie_join_platz.fk_kategorie_id
  =tbl_kategorie.pk_kategorie_id)
WHERE tbl_saalplan_platz.fk_sektor_id=[dieSektorID]
GROUP BY tbl_saalplan_platz.pkp_saalplan_platz_id
```
Bin gespannt, ob es Deinem Vorhaben nahe kommt. (Achtung: in der Ergebnistabelle heißt das Feld nur noch kategorie_id)

Gruß hpvw


----------



## Mik3e (2. Juli 2005)

Hi!

Sorry, bin gestern vorm TV weggesackt 
Werde das gleich mal versuchen... Wenn Du gerne experimentierst, häng ich Dir gerne den SQL Dump der vier Tables an..

LG
Mike


----------



## Mik3e (2. Juli 2005)

Geniale Sache, scheint zu funktionieren (mit ein paar adaptierungen)...
Ich hatte die if -verzweigung auch schon, aber irgendwo einen Hund drinnen...

Werde das jetzt mal ausgiebig mit meiner Applikation testen.

Danke vielmals,
LG
Mike


----------



## Mik3e (3. Juli 2005)

Hi hp!

Also wirklich sauber ist diese Lösung leider nicht..
Denn wenn einem Platz mehrere Kategorien (in verschiedenen Vorstellungen) zugewisen sind, gehts schon nicht mehr...

Grund:
Wenn der Platz zwei mal mit einer Kategorie verknüpft ist, liefert der LEFT Join natürlich auch zwei Datensätze:

*Beispiel:*
_platz 1 -> Kat A (gehört zu der gewünschten Veranstaltung -> Liefert daher "Kat A")
platz 1 -> Kat B (gehört NICHT zu der gewünschten Veranstaltung -> Liefert daher NULL)_

In der IF Verzweigung kommt er nun auch zweimal zu diesem Platz (da er das Select klarerweise erst nach dem JOIN ausführt). Somit setzt er das attribut kategorie_id zuerst auf "Kat A" (was auch stimmen würde), dann aber sofort auf NULL (was nicht stimmt).

D.h. je nach Ordnung der aus dem Join gelieferten Datensätze, gibt mir der Query eine andere kategorie_id zurück.

Eine andere Lösung (was eigentlich mein erster Ansatz war) wäre, dass man den Join mit einer Bedingung verknüpft. D.h.:

_LEFT JOIN `tbl_kategorie` ON tbl_kategorie_join_platz.fk_kategorie_id=tbl_kategorie.pk_kategorie_id AND tbl_kategorie.fk_vorstellung_id=<VORSTELLUNG_ID>_

Er sollte nach meinem Verständnis den JOIN nur dann ausführen, wenn die damit verbundene Bedingung erfüllt ist. Macht er allerdings nicht... 

LG
Mike


----------



## hpvw (3. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Dann versuch doch als zusätzliche Bedingung in der WHERE-Klausel nur die Plätze zur aktuellen Vorstellung zu selektieren:
	
	
	



```
... WHERE (...)
AND (tbl_kategorie.fk_vorstellung_id=[dieVorstellungID])
...
```
Gruß hpvw


----------



## Mik3e (3. Juli 2005)

Das geht deshalb nicht, weil er mir dann nur jene Datensätze liefert, die mit einer Kategorie verknüpft sind. Aber er soll mir ja ALLE Plätze des Sektors liefern, und nur bei denen, die bei der betroffenen Vorstellung mit einer Kategorie verknüpft sind auch die KategorieID...


----------



## hpvw (3. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Na, dann nimmt man das halt mit rein:

```
... WHERE (...)
AND (tbl_kategorie.fk_vorstellung_id=[dieVorstellungID]
  OR tbl_kategorie.fk_vorstellung_id IS NULL)
...
```


----------



## Mik3e (3. Juli 2005)

Geht auch nicht.. Denn wenn ein Platz schon bei einer anderen Vorstellung einer Kategorie zugewiesen ist, liefert er den Datensatz nicht mehr aus.

Denn:
WHERE...
...
fk_vorstellung_id`=<VORSTELLUNGID> OR fk_vorstellung_id IS NULL
...

heißt ja, dass er mir den Datensatz liefert wenn:
1. Er entweder gar nicht einer Kategorie zugewiesen ist (ist richtig)
2. Wenn er einer Kategorie zugewiesen ist, dann nur, wenn die Kategorie zur entsprechenden Vorstellung gehört. Ist er jetzt bereits in einer anderen Vorstellung mit einer Kategorie verknüpft, liefert er mir den Datensatz nicht aus, weil:

_fk_vorstellung_id=<VORSTELLUNGID> => FALSE
fk_vorstellung_id IS NULL => FALSE_

Gibts ja nicht, dass es da keine einfache Lösung gibt.... Ich bin schon liecht verzweifelt.


----------



## hpvw (3. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Vielleicht ist das der richtige Moment für HAVING:

```
SELECT
tbl_saalplan_platz.pk_saalplan_platz_id,
tbl_saalplan_platz.fk_sektor_id,
tbl_saalplan_platz.saalplan_platz_element_xpos,
tbl_saalplan_platz.saalplan_platz_element_ypos,
tbl_saalplan_platz.saalplan_platz_element_xbezeichnung,
tbl_saalplan_platz.saalplan_platz_element_ybezeichnung,
tbl_saalplan_platz.saalplan_ist_platz,
if(tbl_kategorie.fk_vorstellung_id=[dieVorstellungID],
  tbl_kategorie_join_platz.fk_kategorie_id,
  NULL) AS kategorie_id
FROM tbl_saalplan_platz
LEFT JOIN tbl_kategorie_join_platz
ON (tbl_saalplan_platz.pk_saalplan_platz_id
  =tbl_kategorie_join_platz.fk_saalplan_platz_id)
LEFT JOIN tbl_kategorie
ON (tbl_kategorie_join_platz.fk_kategorie_id
  =tbl_kategorie.pk_kategorie_id)
HAVING (tbl_kategorie.fk_vorstellung_id=[dieVorstellungID])
WHERE tbl_saalplan_platz.fk_sektor_id=[dieSektorID]
GROUP BY tbl_saalplan_platz.pkp_saalplan_platz_id
```


----------



## Mik3e (3. Juli 2005)

? 
Ist in diesem Zusammenhang ja belanglos ob Where oder Having.. Having dient ja nur dazu, um Funktionen des SQL Queries ansprechen zu können..:
Wenn ich das so implementiere gibt er mir wiederum ja nur jene zurück, die bereits einer Kategorie zugewiesen sind...


----------



## hpvw (3. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Ups, sorry, habe mich in der Eile voll verannt. Having ist natürlich Blödsinn, aber evtl. hilft es, die Bedingung mit ins ON zu nehmen: 
	
	
	



```
SELECT
tbl_saalplan_platz.pk_saalplan_platz_id,
tbl_saalplan_platz.fk_sektor_id,
tbl_saalplan_platz.saalplan_platz_element_xpos,
tbl_saalplan_platz.saalplan_platz_element_ypos,
tbl_saalplan_platz.saalplan_platz_element_xbezeichnung,
tbl_saalplan_platz.saalplan_platz_element_ybezeichnung,
tbl_saalplan_platz.saalplan_ist_platz,
if(tbl_kategorie.fk_vorstellung_id=[dieVorstellungID],
  tbl_kategorie_join_platz.fk_kategorie_id,
  NULL) AS kategorie_id
FROM tbl_saalplan_platz
LEFT JOIN tbl_kategorie_join_platz
ON (tbl_saalplan_platz.pk_saalplan_platz_id
  =tbl_kategorie_join_platz.fk_saalplan_platz_id)
LEFT JOIN tbl_kategorie
ON (tbl_kategorie_join_platz.fk_kategorie_id
  =tbl_kategorie.pk_kategorie_id
  AND tbl_kategorie.fk_vorstellung_id=[dieVorstellungID])
WHERE tbl_saalplan_platz.fk_sektor_id=[dieSektorID]
GROUP BY tbl_saalplan_platz.pkp_saalplan_platz_id
```
Ansonsten fällt mir nur noch ein, statt mit tbl_kategorie einen LEFT JOIN zu machen, dort ein Subselect der Tabelle tbl_kategorie einzusetzen, in dem Du nur Kategorien selektierst, die zur entsprechenden Vorstellung gehören.

Gruß hpvw

PS: Wunder Dich nicht, wenn Du bis Mitte nächster Woche nichts von mir hörst ... mein Leben neben dem Forum ruft.


----------



## Mik3e (3. Juli 2005)

Alles klar..
Ich hab das eigentliche Problem lokalisiert...

Wenn wir von dem normalen Left-Join ausgehen, so liefert mir der (ohne Where-Clause) folgenden Recordset (gekürztes beispiel):

PLATZID | KATID | VORID
1 | Kat A | Vor 1
1 | Kat B | Vor 2
2 | NULL | NULL
2 | Kat A | Vor 1
3 | NULL | NULL

Wende ich nun die WHERE-Clause mit folgender Bedingung an:

WHERE
(KATID IS NULL AND VORID IS NULL)
OR
(VORID=1)

wird der Recordset folgendermaßen gekürzt:

1 | Kat A | Vor 1 -> JA
1 | Kat B | Vor 2 -> NEIN
2 | NULL | NULL -> JA
2 | Kat A | Vor 1 -> JA
3 | NULL | NULL -> JA

Und beim Platz mit der ID 2 siehst Du das Problem sehr schön. Er liefert 2 Datensätze, obwohl es nur einer sein darf (da beide Bedingungen erfüllt sind).

LG
Mike


----------



## hpvw (3. Juli 2005)

*Re: MySQL: JOIN zweier Tabellen, deren Datensätze NICHT in der anderen Tabelle vorhan*

Na gut, ein hab ich noch, warte nur noch darauf, dass ich abgeholt werde:

```
SELECT
tbl_saalplan_platz.pk_saalplan_platz_id,
tbl_saalplan_platz.fk_sektor_id,
tbl_saalplan_platz.saalplan_platz_element_xpos,
tbl_saalplan_platz.saalplan_platz_element_ypos,
tbl_saalplan_platz.saalplan_platz_element_xbezeichnung,
tbl_saalplan_platz.saalplan_platz_element_ybezeichnung,
tbl_saalplan_platz.saalplan_ist_platz,
if(tbl_kategorie.fk_vorstellung_id=[dieVorstellungID],
  tbl_kategorie_join_platz.fk_kategorie_id,
  NULL) AS kategorie_id
FROM tbl_saalplan_platz
LEFT JOIN tbl_kategorie_join_platz
ON (tbl_saalplan_platz.pk_saalplan_platz_id
  =tbl_kategorie_join_platz.fk_saalplan_platz_id)
LEFT JOIN tbl_kategorie
ON (tbl_kategorie_join_platz.fk_kategorie_id
  =tbl_kategorie.pk_kategorie_id
  AND tbl_kategorie.fk_vorstellung_id=[dieVorstellungID]
#Sieht blöd aus, aber die Idee ist, dass er die Zeilen,
#in denen es null ist raus nimmt und wenn es keine 
#entsprechene Zeile gibt wieder (LEFT JOIN) 
#eine mit null einfügt
#evtl. musst Du das mal mit anderen Attributen aus einer
#anderen Tabelle probieren oder auch sonst irgendwie 
#mit den anderen Ideen kombinieren
#... ich muss los Tschüss
  AND tbl_kategorie.fk_vorstellung_id IS NOT NULL)
WHERE tbl_saalplan_platz.fk_sektor_id=[dieSektorID]
GROUP BY tbl_saalplan_platz.pkp_saalplan_platz_id
```


----------



## Mik3e (3. Juli 2005)

Danke... genieß die IT freien Tage 
Ich werd das Teil jetzt einfach in zwei Methoden zerlegen:
1. Liefert mir die Platzinformation
2. Prüft, ob der jeweilige Platz mit einer Kategorie belegt ist.
Fertig..

Die zwei SQL Statements sind unterm Strich wahrscheinlich auch schneller als das eine mächtig große 

LG
Mike


----------

