Geht das mit MySQL JOINS?
MightyMike
- datenbank
0 André Laugks0 Jörg Peschke0 dedlfix
0 MightyMike0 dedlfix
Hallöderle,
nachdem ich JOINS für mich entdeckt habe - die sind echt toll ;o) - habe ich die Datenbankanfragen auf meiner HP mal total umgestellt. Allerdings hätte bei einem JOIN von folgender Art von Tabellen gerne mal ein Problem.
Nehmen wir als Beispiel meine Newsseite: Dafür werden in einer Tabelle die News mit einer eineindeutigen ID abgespeichert - welch Wunder - und in einer weiteren Tabelle Links zu den News. Da es zu jeden News unterschiedlich viele Links geben kann wird in dieser Tabelle ein Datensatz pro Link angelegt (sprich die News-ID, damit klar ist, wozu der Link gehört und natürlich der Link selber). Es gibt z.B. auch noch Kommentare zu den jeweiligen News, die auf vergleichbare Weise in einer separaten Tabelle abgespeichert werden, aber da tritt ja das gleiche Problem auf: Kann ich gleichzeitig die News und alle Links (und alle Kommentare) mit einer Datenbankanfrage herausholen? (das einzige was mir dazu - evtl. - einfällt wären SubSelects; aber so wie ich diese verstehe reduziert das den Anfragenaufwand dadurch nicht). Habt ihr einen Vorschlag?
Ich hätte ja selbst nach Lösungen gesucht, aber ich weiß leider nicht, wonach ich suchen soll. Mit 'nem Link zu einem passenden Thema wäre mir schon sehr geholfen.
Vielen Dank
Mighty
Hallo!
Kann ich gleichzeitig die News und alle Links (und alle Kommentare) mit einer Datenbankanfrage herausholen? (das einzige was mir dazu - evtl. - einfällt wären SubSelects; aber so wie ich diese verstehe reduziert das den Anfragenaufwand dadurch nicht). Habt ihr einen Vorschlag?
Ja, ohne Probleme. Du hast ja die ID mit denen alle Datensätze Verknüpft sind:
Bsp:
SELECT
*
FROM
news AS n
LEFT JOIN
links AS l
ON
(n.id_news=l.id_news)
LEFT JOIN
kommentare AS k
ON
(n.id_news=k.id_news);
Arbeite am besten mit LEFT/RIGHT JOINs, falls es keine Links oder Kommentare zu einer News gibt.
André Laugks
Hallo Jörg und André,
also ich denke nicht, dass Eure beiden Ideen so funktionieren, wie ich mir das vorstelle. Vielleicht habe ich mich nur zu unklar ausgedrückt. Machen wir mal ein Beispiel:
In der Tabelle 'news' steht z.B.:
ID|Titel|Inhalt
73|Testnews|Hallo Welt!
Zu diesen News gebe es jetzt zwei Links. Das sieht das dann in der Tabelle 'links' so aus
NewsID|Link|Titel
73|www.seite1.de|Seite1
73|www.seite2.de|Seite2
Wenn ich das jetzt so (Variante André)
> SELECT
> *
> FROM
> news AS n
> LEFT JOIN
> links AS l
> ON
> (n.ID=l.NewsID)
oder so (Variante Jörg)
> SELECT * FROM news,links
> WHERE links.NewsID = news.ID;
aufrufe (beide Varianten schon zuvor getestet), dann tut es nicht das, was ich will, das es tut. Das mit den JOINS funktioniert imho dann und nur dann so wie es soll, wenn es zu einem Eintrag in einer Tabelle auch genau einen anderen Eintrag in einer anderen Tabelle gibt. Wenn ich das richtig sehe, würden in Euren Codeschnipseln die News mit der ID 73 gleich zwei mal in die Ergebnistabelle eingetragen mit jeweils einem der Links. Und das ist denke ich nicht Sinn der Sache. Was ich aber gerne hätte ist, dass beide Links in einem Datensatz zu den News einmal der Ergebnistabelle drinstehen.
nachdem ich JOINS für mich entdeckt habe - die sind echt toll ;o)
Nur leider auch toll teuer (also performanz-technisch)
- aber wenn man's in Grenzen hält...
Echt? Wieso das? Ich meine immer noch besser als zwei MySQL Abfragen derart
SELECT * FROM news;
und dann in einer Schleife die alle News durchläuft jeweils eine Anfrage
SELECT * FROM links WHERE NewsID = $ergebnis['ID'];
zu machen. Wie löst Du das solche Probleme denn? Bzw. wie würdest Du es machen?
LG
MightyMike
Hallo!
aufrufe (beide Varianten schon zuvor getestet), dann tut es nicht das, was ich will, das es tut. Das mit den JOINS funktioniert imho dann und nur dann so wie es soll, wenn es zu einem Eintrag in einer Tabelle auch genau einen anderen Eintrag in einer anderen Tabelle gibt. Wenn ich das richtig sehe, würden in Euren Codeschnipseln die News mit der ID 73 gleich zwei mal in die Ergebnistabelle eingetragen mit jeweils einem der Links. Und das ist denke ich nicht Sinn der Sache. Was ich aber gerne hätte ist, dass beide Links in einem Datensatz zu den News einmal der Ergebnistabelle drinstehen.
Das ist doch logisch, dass bei mehreren Links die News mehrmals in der Ergebnisliste vorkommt! Du mußt aus der Ergebnisliste in Deiner Applikation alles richtig rausholen. Das macht man mich Schleifen und Arrays.
$i = 0;
foreach($rows AS $row) {
$news['news']['title'] = $row['title'];
$news['news']['text'] = $row['text'];
// Links
$news['links'][] = $row['link'];
// Kommentare
$news['kommentar'][$i]['title'] = $row['kommentar_titel'];
$news['kommentar'][$i]['text'] = $row['kommentar_text'];
$i;
}
echo $news['news']['title'];
echo $news['news']['text'];
foreach($news['links'] AS $link) {
echo $link;
}
foreach($news['kommentar'] AS $kommentar) {
echo $kommentar['title'] . "<br/>";
echo $kommentar['text'] . "<br/>";
echo "<hr/>";
}
André Laugks
Das macht man mich Schleifen und Arrays [...]
Danke. Ist mir schon klar, dass ich die gewünschten Infos so rausholen kann, ich hatte halt gehofft, das bereits im Vorfeld eleganter über MySQL lösen zu können. Daher auch der Titel des Threads ;o). Hierbei besteht aber nun das Problem, dass solche News, die keine Links UND keine Kommentare haben, nicht im Ergebnis auftauchen. Wie ich schon oben in meiner Reaktion auf dedlfix Beitrag gefragt habe: Kann ich erzwingen, dass auf jeden Fall alle News in der Tabelle drin sind, ob nun Links oder Kommentare vorhanden sind oder nicht?
Hallo!
Hierbei besteht aber nun das Problem, dass solche News, die keine Links UND keine Kommentare haben, nicht im Ergebnis auftauchen.
Deshalb einen LEFT JOIN!
André Laugks
Tag,
nachdem ich JOINS für mich entdeckt habe - die sind echt toll ;o)
Nur leider auch toll teuer (also performanz-technisch) - aber wenn man's in Grenzen hält...
Habt ihr einen Vorschlag?
Hm...
SELECT * FROM news,links
WHERE links.newsID = news.newsID;
Tut das nicht?
echo $begrüßung;
Kann ich gleichzeitig die News und alle Links (und alle Kommentare) mit einer Datenbankanfrage herausholen?
Die Vorschläge, News und Links miteinander zu verjoinen, halte ich nicht für besonders hübsch.
Da es zu jeden News unterschiedlich viele Links geben kann [...]
... enthält das Ergebnis dann für jeden Link auch die zugehörige News. Das heißt, bei 5 Links zur News hast du die News fünfmal im Ergebnis. Für jede News eine eigene Abfrage nach den Links zu starten, ist auch nicht besonders ressourcenschonend. Zwei Vorschläge hätte ich im Angebot:
Wenn sich die Anzahl der abgefragten News im Rahmen hält, könnte eine zweite Abfrage mit WHERE ID_News IN (..., ..., ...) die zugehörigen Links ermitteln. Du müsstest dann bei Abfragen der Ergebnisdatensätze selbige anhand der News-ID den News zuordnen.
GROUP_CONCAT() (nicht in allen MySQL-Versionen verfügbar) verknüpft die durch die Gruppierung zusammengefassten Werte zu einem String, den man sich in der abfragenden Umgebung (z.B. PHP) am selbst gewählten Separator wieder aufsplitten lassen kann.
SELECT ID, Text, GROUP_CONCAT(Links.Link SEPARATOR '||') AS Links
FROM News
LEFT JOIN Links ON News.ID = Links.ID_News
GROUP BY News.ID
Normalerweise sollen bei gruppierten Abfragen außer Aggregatfunktionen nur die in der Gruppierungsbedingung angegebenen Spalten in der SELECT-Klausel auftauchen, da mitunter nicht entschieden werden kann, welcher der Datensätze pro Gruppe nun ausgegeben werden soll. Andere Systeme schränken das dementsprechend ein, MySQL jedoch nicht (hat aber eine entsprechende Warnung im Handbuch stehen). In unserem Fall ist durch die Gruppierungsbedingung News.ID und einen eindeutigen Index auf diesem Feld sichergestellt, dass nur ein Datensatz pro Gruppe existiert. Wir können hier also die Freizügigkeit MySQLs ohne Nebenwirkungen nutzen.
Als Separator wählte ich ||. Es kann aber auch jede beliebige andere Zeichenfolge sein, von der anzunehmen ist, dass sie nicht innerhalb eines Links auftaucht. An diesem Separator kann man die Links wieder trennen. PHP: explode('||', $links) Python: links.split('||') usw.
echo "$verabschiedung $name";
Die Vorschläge, News und Links miteinander zu verjoinen, halte ich nicht für besonders hübsch.
ne bessere Idee? her damit! :o)
... enthält das Ergebnis dann für jeden Link auch die zugehörige News. Das heißt, bei 5 Links zur News hast du die News fünfmal im Ergebnis.
Das ist das Problem.
Wenn sich die Anzahl der abgefragten News im Rahmen hält, könnte eine zweite Abfrage mit WHERE ID_News IN (..., ..., ...) die zugehörigen Links ermitteln. Du müsstest dann bei Abfragen der Ergebnisdatensätze selbige anhand der News-ID den News zuordnen.
Das ist meine bisherige Lösung. Die Anzahl hält sich zwar in Grenzen, aber wenn ich den Code übersichtlicher schreiben kann, dann versuche ich das.
GROUP_CONCAT() (nicht in allen MySQL-Versionen verfügbar) verknüpft die durch die Gruppierung zusammengefassten Werte zu einem String, den man sich in der abfragenden Umgebung (z.B. PHP) am selbst gewählten Separator wieder aufsplitten lassen kann.
> SELECT ID, Text, GROUP_CONCAT(Links.Link SEPARATOR '||') AS Links
> FROM News
> LEFT JOIN Links ON News.ID = Links.ID_News
> GROUP BY News.ID
Ich denke, damit komme ich auf den richtigen Weg. Habe schonmal ein bisschen probiert. Problem ist nun - wie generell bei JOINS, glaube ich - das nun nur solche News auftauchen, die auch tatsächlich Links haben. News ohne Links sind nun nicht mehr in der Ergebnistabelle enthalten. Kann ich irgendwie erzwingen, auf jeden Fall alle News im Ergebnis zu haben?
echo $begrüßung;
FROM News
LEFT JOIN Links ON News.ID = Links.ID_News
Problem ist nun - wie generell bei JOINS, glaube ich - das nun nur solche News auftauchen, die auch tatsächlich Links haben. News ohne Links sind nun nicht mehr in der Ergebnistabelle enthalten. Kann ich irgendwie erzwingen, auf jeden Fall alle News im Ergebnis zu haben?
Das ist in meiner Abfrage schon mit eingebaut und lässt sich am Wörtchen LEFT erkennen. Das Ergebnis enthält dann alle Datensätze der links vom JOIN notierten Tabelle (hier News) und wenn vorhanden die Daten aus der rechten Tabelle (Links) oder NULL für die Felder, die eigentlich mit den Daten der rechten hätten befüllt werden sollen. LEFT kann man auch weglassen, da das der Standard-JOIN ist. Deine Befürchtungen treten nur dann ein, wenn du einen INNER JOIN nimmst. Der entsteht auch, wenn der Join implizit über die WHERE-Klausel notiert wird.
echo "$verabschiedung $name";