SQL Abfrage performance
Gerd H.
- datenbank
2 Vinzenz Mai0 Gerd H.
Hallo,
es geht um ein Forum. Da habe ich eine Übersicht über die letzten Beiträge. Funktioniert auch, nur dass da viel zu viele abfragen gemacht werden. Bin mir sicher, dass es auch besser geht, nur nicht wie :)
Hier die aktuelle Version:
// Hier die Beiträge
SELECT beitrag.thread_id, thread.forum_id, thread.beschreibung, thread.titel, beitrag.time, user.username
FROM thread, beitrag, user
WHERE thread.id=beitrag.thread_id AND beitrag.von_id=user.id AND thread.lasttime=beitrag.time AND thread.forum_id!='13' AND thread.forum_id!='14'
GROUP BY beitrag.thread_id
ORDER by thread.lasttime DESC LIMIT 8
In der Schleife wo die Beiträge ausgelesen werden wird bei jedem Durchlauf folgende Abfrage gemacht, damit ich die anzahl der Gesamtbeiträge bekomme, um einen Link auf die letzte Seite des Threads zu setzen:
SELECT beitrag.b_id
FROM beitrag, thread, user
WHERE beitrag.thread_id='" . mysql_real_escape_string($thread_id) . "' AND thread.forum_id='" . mysql_real_escape_string($forum_id) . "' AND beitrag.thread_id=thread.id AND beitrag.von_id=user.id
Das sind dann insgesammt bei jedem Aufruf nur an dieser Stelle 9 Abfragen, wo bestimmt auch eine sein könnte :)
Kann mir da jemand was basteln :)
Danke Gerd
Hallo Gerd,
es geht um ein Forum. Da habe ich eine Übersicht über die letzten Beiträge. Funktioniert auch, nur dass da viel zu viele abfragen gemacht werden. Bin mir sicher, dass es auch besser geht, nur nicht wie :)
es ist möglich, dass es möglich ist.
Es ist möglich, dass es nicht möglich ist.
Das hängt davon ab, was Dein Datenbankmanagementsystem (DBMS) kann.
SELECT beitrag.thread_id, thread.forum_id, thread.beschreibung, thread.titel, beitrag.time, user.username
FROM thread, beitrag, user
WHERE thread.id=beitrag.thread_id AND beitrag.von_id=user.id AND thread.lasttime=beitrag.time AND thread.forum_id!='13' AND thread.forum_id!='14'
GROUP BY beitrag.thread_id
ORDER by thread.lasttime DESC LIMIT 8
Dieses Statement sagt mir, dass es sich bei Deinem DBMS um MySQL handelt. Jedes andere DBMS weist Dein Statement als fehlerhaft zurück. Schreiben wir es ein wenig mit expliziter Join-Syntax, die ich persönlich wesentlich lesbarer finde, um:
~~~sql
SELECT
b.thread_id,
t.forum_id,
t.beschreibung,
b.time,
u.username
FROM thread t -- Tabellen-Aliasnamen ersparen Schreibarbeit
-- und können die Übersicht erhöhen
INNER JOIN beitrag b -- explizite JOIN-Syntax
ON t.id = b.thread_id
INNER JOIN user u
ON b.von_id = u.id
WHERE -- damit lässt sich die Einschränkung viel besser sehen
t.forum_id NOT IN (13, 14) -- NOT IN finde ich übersichtlicher als Deine Und-Verknüpfung
-- von Ungleichheiten.
GROUP BY
b.thread_id, -- In der Spaltenliste dürfen _nur_ Spalten
t.forum_id, -- auftreten, nach den gruppiert wird oder
t.beschreibung, -- auf die eine Aggregatsfunktion angewandt wird.
b.time, -- Da Du keine einzige Aggregatsfunktion anwendest,
u.username -- muss nach allen Spalten gruppiert werden.
ORDER BY t.lasttime DESC
LIMIT 8
Wenn diese Abfrage nicht das gewünschte Ergebnis liefert, dann liefert Dir Deine Abfrage nur zufälligerweise das gewünschte Ergebnis und kann auch andere Ergebnisse zurückliefern. Siehe Handbuch.
Ich halte es für keine gute Idee, Spalten bequemlichkeitshalber in der GROUP-BY-Klausel wegzulassen. Es führt oft zu Fehlern, wozu es eine Menge Threads im Archiv gibt.
Ich habe meine Zweifel, ob Deine Spalte
beitrag.time
wirklich garantiert den richtigen Wert aufweist. Ich vermute, das ist nicht der Fall. Vermutlich solltest Du diesen Wert (wahrscheinlich die Zeit des ersten Beitrags im Thread) und auch den Benutzernamen zu diesem Beitrag mit einem korrelierten Subselect ermitteln.
In der Schleife wo die Beiträge ausgelesen werden wird bei jedem Durchlauf folgende Abfrage gemacht, damit ich die anzahl der Gesamtbeiträge bekomme, um einen Link auf die letzte Seite des Threads zu setzen:
Eine Anzahl von Gesamtbeiträgen kann man normalerweise bequem mit einem Subselect ermitteln. Da ich Deine Tabellenstruktur nicht kenne, kann ich Dir nicht weiterhelfen. Da müssen mehr Informationen her, am besten die relevanten Spalten Deiner drei Tabellen mit ein paar Beispieldatensätzen, dazu das gewünschte Resultat - mit der Begründung, warum Du genau dieses haben möchtest.
Bevor ich es vergesse: MySQL unterstützt Subselects ab Version 4.1, siehe verlinkter Handbuchabschnitt. Deshalb wäre es eine gute Idee von Dir, uns zu sagen, welche Version Du verwendest.
Freundliche Grüße
Vinzenz
Leider erhalte ich nicht das gewünschte Ergebnis. Aber danke für die sehr ausführliche Antwort.
Als Ergebnis erhalte ich mehrere Datensätze des selben Themas und da soll ja pro Thema maximal ein Ergebnis des letzen Beitrags angzeigt werden.
Außerdem brauchte deine Abfrage ca. 0,65 Sekunden ...meine im Vergleich dazu weniger als die Hälfte der Zeit: 0,24 Sekunden
Aber deine finde ich echt übersichtlicher :)
Hier die Struktur meiner Tabellen:
mysql> SELECT * FROM beitrag LIMIT 1;
+------+-----------+--------+-----------+----------+---------------------------------+------------+----+
| b_id | thread_id | von_id | edit_time | edit_von | kommentar | time | ip |
+------+-----------+--------+-----------+----------+---------------------------------+------------+----+
mysql> SELECT * FROM thread LIMIT 1;
+----+---------+--------------------+---------------------------------------+--------+----------+---------+------------+
| id | aufrufe | titel | beschreibung | closed | forum_id | wichtig | lasttime |
+----+---------+--------------------+---------------------------------------+--------+----------+---------+------------+
Und ja es geht hier um MySQL 4.1 ! Ich vergesse es echt immer anzugeben :)
Als ergebnis möchte ich halt die letzten Beiträge haben. Zusätzlich brauche ich die Anzahl der Beiträge in dem jeweiligen Thread, damit ich einen direkten Link auf die letzte Seite setzen kann.
PS: das mit den "NOT IN" werde ich jetzt schon übernehmen :)