Seite 1 von 1

SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 18:48
von SemiX
Also eigentlich hatte ich gehofft, dass ich selbst drauf komme, aber nun...bin ich mit meinem Latein am Ende :oops: Ursache allen Übels ist folgende SQL-Abfrage:

Code: Alles auswählen

    $sql = "SELECT t.topic_id
  	FROM " . TOPICS_TABLE . " t, " . POSTS_TABLE . " p, " . VOTE_DESC_TABLE . " v
  	WHERE (t.topic_vote = 0
      AND t.topic_status < 2
  		AND t.topic_last_post_id = p.post_id
      AND p.post_time < $archive_topics_older_than)

      OR  (t.topic_vote = 1
      AND t.topic_status < 2
  		AND t.topic_last_post_id = p.post_id
      AND p.post_time < $archive_topics_older_than
      AND t.topic_id = v.topic_id
      AND v.vote_start+v.vote_length < " . time() . ")";
Nun, was soll sie machen? Im Prinzip will ich die Themen ausgeben, die...
a.) ...älter als z.B. 90 Tage sind und keine Umfrage besitzen und
b.) ...älter als z.B. 90 Tage sind und eine Umfrage besitzen, die bereits abgelaufen ist
(unbegrenzt lange gehende Umfragen sind hier nicht berücksichtigt)

Verarbeitet wird das dann durch eine while- und for-Schleife, die problemlos funktioniert. Der obige Schnippsel tuts auch, was er soll, nur gibt er mir die entsprechenden Themen doppelt aus. Sprich habe ich die Themen 1,2,3 und Thema 3 ist eine zeitlich begrenzte Umfrage, dann verarbeitet die for-Schleife die topic_id's in dieser Reihenfolge: 1,2,1,2,3.


Ich habe überlegt, ob ich das OR vielleicht falsch angewendet habe. Daher habe ich die sql-Abfrage mal auf folgendes reduziert:

Code: Alles auswählen

  $sql = "SELECT t.topic_id
  	FROM " . TOPICS_TABLE . " t, " . POSTS_TABLE . " p, " . VOTE_DESC_TABLE . " v
  	WHERE t.topic_vote = 0
      AND t.topic_status < 2
  		AND t.topic_last_post_id = p.post_id
      AND p.post_time < $archive_topics_older_than";
Nun gibt mir der Scherzkeks aber die topic_id's 1,2,1,2 aus. Erst wenn ich das , " . VOTE_DESC_TABLE . " v rausnehme, macht ers richtig. Und da ich prinzipiell neugierig bin: Wo hab ich den Wald in Brand gesetzt?

Danke schonmal, SemiX :)


EDIT: Auf den Rat von cYbercOsmOnauT hin Klammern eingefügt...

Verfasst: 17.08.2006 20:42
von cYbercOsmOnauT
Bevor ich mir Deinen Query genauer ansehe, hier ein erster Rat: Verwende bei solch komplexen boolschen Operationen immer Klammern. Damit Du sicher gehst, das diese auch richtig ausgeführt werden.

Beispiel:
a AND b OR c AND d

Weißt Du aus dem Kopf welche Wertigkeit welche boolsche Operation hat und somit zuerst durchgeführt wird? Ich nicht. Dann lieber so schreiben:
(a AND b) OR (a AND d)
und Du bist auf der sicheren Seite.

Grüße,
Tekin

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 21:00
von Banger
SemiX hat geschrieben:

Code: Alles auswählen

  $sql = "SELECT t.topic_id
  	FROM " . TOPICS_TABLE . " t, " . POSTS_TABLE . " p, " . VOTE_DESC_TABLE . " v
  	WHERE t.topic_vote = 0
      AND t.topic_status < 2
  		AND t.topic_last_post_id = p.post_id
      AND p.post_time < $archive_topics_older_than

      OR  t.topic_vote = 1
      AND t.topic_status < 2
  		AND t.topic_last_post_id = p.post_id
      AND p.post_time < $archive_topics_older_than
      AND t.topic_id = v.topic_id
      AND v.vote_start+v.vote_length < " . time();
Hi Semix,
vorerst würde ich die Query mal aufräumen, zwecks Übersichtlichkeit:

Code: Alles auswählen

$sql = 'SELECT t.topic_id
          FROM '.TOPICS_TABLE.' AS t
          JOIN '.POSTS_TABLE.' AS p
            ON p.post_id = t.last_post_id
           AND p.post_time < '.$archive_topics_older_than.'
     LEFT JOIN '.VOTE_DESC_TABLE.' AS v
            ON v.topic_id = t.topic_id
         WHERE t.topic_vote BETWEEN 0 AND 1
           AND t.topic_status < 2
           AND (v.topic_id IS NULL
                OR
                v.vote_start+v.vote_length < '.time().')
      GROUP BY t.topic_id';
Diese Form ist ressourcensparender (vulgo: schneller :-)), da der JOIN zur (recht großen) POSTS_TABLE erst nach Erfüllung der WHERE-Bedingung erfolgt. Zudem sind die Bedingungen so kompakter; die doppelten Bedingungen aus Deinem OR-Zweig können so auf jeweils eine reduziert werden.

Gerade bei einer Mischung von AND und OR empfehle ich immer, Klammern zu verwenden, das erhöht die Übersichtlichkeit und minimiert die Fehleranfälligkeit phänomenal. Das war bei Dir aber nicht ausschlaggeben, und um auf den Punkt zu kommen: das GROUP BY fehlte :-D

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 21:36
von SemiX
Diese Form ist ressourcensparender (vulgo: schneller :-)), da der JOIN zur (recht großen) POSTS_TABLE erst nach Erfüllung der WHERE-Bedingung erfolgt. Zudem sind die Bedingungen so kompakter; die doppelten Bedingungen aus Deinem OR-Zweig können so auf jeweils eine reduziert werden.
Danke danke danke! Ich könnt dich knuddeln :D

Group by - arg nie benutzt :oops: Über Join hab ichs auch probiert nur...ich blick da noch nicht durch die Syntax, also hab ichs wieder verworfen. Selbst mein Wälzer hier hilft mir da nicht weiter. Mal sehen ob ich irgendwas Erklärfreundliches im Netz finde... *hust*

SemiX

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 21:45
von Banger
SemiX hat geschrieben:Über Join hab ichs auch probiert nur...ich blick da noch nicht durch die Syntax, also hab ichs wieder verworfen. Selbst mein Wälzer hier hilft mir da nicht weiter. Mal sehen ob ich irgendwas Erklärfreundliches im Netz finde... *hust*
Hi SemiX,
vielleicht hilf Dir das hier weiter: http://little-idiot.de/mysql/mysql-118.html

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 22:34
von SemiX
Mal sehn, ist auf jeden Fall ausführlicher als das, was ich bis jetzt so gefunden habe. Aber heute schwirrt mir der Kopf ;)

Noch eine letzte Frage: Irgendeine Idee, wie man den SQL-Code umstellen könnte, damit auch zeitlich unbegrenzte Themen nicht erfasst werden? Die tragen ja 0 als Wert für vote_length und sind damit kleiner als der aktuelle Timestamp.. :-?

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 22:39
von Banger
SemiX hat geschrieben:Irgendeine Idee, wie man den SQL-Code umstellen könnte, damit auch zeitlich unbegrenzte Themen nicht erfasst werden? Die tragen ja 0 als Wert für vote_length und sind damit kleiner als der aktuelle Timestamp.. :-?
So?

Code: Alles auswählen

$sql = 'SELECT t.topic_id
          FROM '.TOPICS_TABLE.' AS t
          JOIN '.POSTS_TABLE.' AS p
            ON p.post_id = t.last_post_id
           AND p.post_time < '.$archive_topics_older_than.'
     LEFT JOIN '.VOTE_DESC_TABLE.' AS v
            ON v.topic_id = t.topic_id
         WHERE t.topic_vote BETWEEN 0 AND 1
           AND t.topic_status < 2
           AND (v.topic_id IS NULL
                OR
                (v.vote_length > 0 
                AND
                v.vote_start+v.vote_length < '.time().'))
      GROUP BY t.topic_id';

Re: SQL-Abfrage mit OR - aber wie?

Verfasst: 17.08.2006 22:51
von SemiX
Banger hat geschrieben: So? [Codebox]
arg [ externes Bild ] - womit bewiesen wäre, dass meine Konzentration zum gegenwärtigen Zeitpunkt wirklich Banane ist. Nochmals Danke! :grin:

SemiX