开发者

Building a long query and have a lot of if statements - is there a more elegant way?

I have to build a query based on certain conditions. Is there a better way of doing it than the way I have done below? It works fine but I can see it getting out of hand fairly quickly if there were more conditions since I check if any previous conditions had been met ever开发者_JS百科y time I check a new one.

    $sql = "SELECT DISTINCT fkRespondentID FROM tblRespondentDayTime";

    if (!empty($day) || !empty($time) || !empty($sportID)) {

        $sql .= " WHERE";

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

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

        if (!empty($sportID)) {
            if (!empty($day) || !empty($time)) {
                $sql .= " AND";
            }
            $sql .= " fkRespondentID IN (SELECT fkRespondentID FROM tblRespondentSport WHERE fkSportID='$sportID')";
        }

    }


I would use the old "WHERE 1=1" trick; add this as the first condition, and then you can assume the "AND" condition on each statement that follows.


$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')";


Build a list/array of conditions, where each conditional is optional (i.e. if condition is valid, push it on the list).

If this list is > 0, add "where" and then add the list join'ed by "and".


Rather than doing checks like if (!empty($day) || !empty($time)) you can create a $whereClause variable and check it like this:

$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;

This will also work if you need to, say, change some to an OR (1=1 trick won't work in that case and could even prove quite hazardous).


you could try putting your variables in an array and having a boolean that tells if you need to add the "AND" in before your next phrase. This would shorten your control statements to a foreach and a nested if.


Here is my solution:

$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));
}

Please note, that because of the closures, this won't work below PHP 5.3. If you are using an older PHP, make the closure as a separate function, or substitute it with a foreach.


Unfortunately, building dynamic SQL is a tedious experience and even if you can change a few things in your logic (which actually looks relatively clean), it's still going to be ugly.

Fortunately, Object-relational mapping exists. I'm not too familiar with PHP, but Perl has several CPAN modules such as SQL::Abstract, which will allow you to build fairly complex SQL statements using basic data structures.


If you use stored procedures, you can do something like this:

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 for _day means any fldDay is OK. Any other value for _day, and it must be matched. I assumed fldDay is text, but of course you can type everything properly here.

I know some people aren't fans of stored procedures, but it can be handy encapsulate query logic this way.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜