2010-08-12 1 views
7

Ich muss eine Abfrage basierend auf bestimmten Bedingungen erstellen. Gibt es einen besseren Weg als ich es unten getan habe? Es funktioniert gut, aber ich kann sehen, dass es ziemlich schnell außer Kontrolle gerät, wenn es mehr Bedingungen gibt, da ich jedes Mal, wenn ich eine neue überprüfe, nachprüfte, ob irgendwelche vorherigen Bedingungen erfüllt wurden.Erstellen einer langen Abfrage und viele if-Anweisungen - gibt es einen eleganteren Weg?

+0

Es ist eine gute Frage :) – dmp

Antwort

6

Ich würde den alten "WHERE 1=1" Trick verwenden; Fügen Sie dies als erste Bedingung hinzu, und Sie können dann die UND-Bedingung für jede folgende Anweisung annehmen.

+0

@Palpie hat praktische Anwendung dieser Methode in seiner Antwort demonstriert. – DanP

0

Sie könnten versuchen, Ihre Variablen in ein Array zu setzen und mit einem Boolean, die sagt, ob Sie die "UND" vor Ihrer nächsten Phrase hinzufügen müssen. Dies würde Ihre Steueranweisungen auf eine foreach und eine geschachtelte if verkürzen.

0

Hier ist meine Lösung:

$sql = "SELECT * FROM table"; 
$conditions = array(
    'fldDay' => $day, 
    'fldTime' => $time, 
); 

if (count(array_filter($conditions))) { 
    $sql .= ' WHERE '; 
    $sql .= implode(' AND ', array_map(function($field, $value) { 
    return $field . '=\'' . pg_escape_string($value) . '\''; 
    }, array_keys($conditions), $conditions)); 
} 

Bitte beachten Sie, dass aufgrund der Schließungen, wird dies nicht unter PHP 5.3 arbeiten. Wenn Sie einen älteren PHP verwenden, machen Sie den Abschluss als separate Funktion oder ersetzen Sie ihn durch eine foreach.

+0

Dieser Code ist schwer zu lesen. –

1

Erstellen Sie eine Liste/ein Array von Bedingungen, wobei jede Bedingung optional ist (d. H. Wenn die Bedingung gültig ist, drücken Sie sie in der Liste).

Wenn diese Liste> 0 ist, fügen Sie "wo" hinzu und fügen Sie dann die mit "und" verbundene Liste hinzu.

0

Leider ist der Aufbau von dynamischem SQL eine mühsame Erfahrung und selbst wenn Sie ein paar Dinge in Ihrer Logik ändern können (die eigentlich relativ sauber aussieht), wird es immer noch hässlich sein.

Glücklicherweise existiert Object-relational mapping existiert. Ich bin nicht vertraut mit PHP, aber Perl hat mehrere CPAN-Module wie SQL :: Abstract, mit denen Sie ziemlich komplexe SQL-Anweisungen mit grundlegenden Datenstrukturen erstellen können.

+0

Wir wissen nicht die Größe des Systems, das Birderic erstellt. Möglicherweise sind ORMs und SQL-Konstruktoren hier nicht optimal. –

1

Anstatt Kontrollen wie if (!empty($day) || !empty($time)) tun können Sie eine $whereClause Variable erstellen und es wie folgt überprüfen:

$sql = "SELECT DISTINCT fkRespondentID 
     FROM tblRespondentDayTime"; 

$whereClause = ''; 

// fldDay 
if (!empty($day)) { 
    $whereClause .= " fldDay='$day'"; 
} 

// fldTime 
if (!empty($time)) { 
    if (!empty($whereClause)) { 
     $whereClause .= ' AND '; 
    } 
    $whereClause .= " fldTime='$time'"; 
} 

// fkRespondentID 
if (!empty($sportID)) { 
    if (!empty($whereClause)) { 
     $whereClause .= ' AND '; 
    } 
    $whereClause .= " fkRespondentID IN (SELECT fkRespondentID 
             FROM tblRespondentSport 
             WHERE fkSportID='$sportID')"; 
} 

if (!empty($whereClause)) { 
    $whereClause = ' WHERE '.$whereClause; 
} 

$sql .= $whereClause; 

Dies wird auch funktionieren, wenn Sie brauchen, um, sagen wir, etwas zu einem OR (1 = 1 Trick ändern funktioniert in diesem Fall nicht und könnte sich sogar als sehr gefährlich erweisen).

4
$sql = "SELECT DISTINCT fkRespondentID FROM tblRespondentDayTime WHERE 1=1"; 

if (!empty($day)) 
    $sql .= "AND fldDay='$day'"; 

if (!empty($time)) { 
    $sql .= "AND fldTime='$time'"; 

if (!empty($sportID)) 
    $sql .= "AND fkRespondentID IN (SELECT fkRespondentID FROM tblRespondentSport WHERE fkSportID='$sportID')"; 
+0

Danke für die Beispielimplementierung + 1 – DanP

0

Wenn Sie gespeicherte Prozeduren verwenden, können Sie etwas tun:

CREATE PROCEDURE `FindRespondents` (
    IN `_day` varchar(255), 
    ... 
) 
BEGIN 
    SELECT DISTINCT fkRespondentID 
    FROM tblRespondentDayTime 
    WHERE (_day Is Null OR fldDay = _day) 
     AND ... 
END; 
| 

Passing in null für _day bedeutet jede fldDay OK ist. Jeder andere Wert für _day, und es muss abgestimmt werden. Ich habe angenommen, fldDay ist Text, aber natürlich können Sie hier alles richtig eingeben.

Ich weiß, dass einige Leute keine Fans von gespeicherten Prozeduren sind, aber es kann praktisch sein, Abfrage Logik auf diese Weise zu kapseln.

+0

Ich denke, es würde auch mit vorbereiteten Anweisungen funktionieren, wenn Sie die where-Klausel auf diese Weise schreiben und jeden dieser Parameter an '?' S binden. – grossvogel