构建一个长查询并有很多if语句 - 是否有更优雅的方式?

时间:2010-08-12 18:52:41

标签: php mysql refactoring coding-style

我必须根据某些条件构建查询。有没有比我在下面做的更好的方式呢?它工作正常,但是如果有更多的条件,我可以看到它相当快地失控,因为我检查每次检查新的条件是否满足任何先前的条件。

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

    }

8 个答案:

答案 0 :(得分:6)

我会使用旧的"WHERE 1=1"技巧;将此作为第一个条件添加,然后您可以对随后的每个语句采用“AND”条件。

答案 1 :(得分: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')";

答案 2 :(得分:1)

构建条件列表/数组,其中每个条件都是可选的(即如果条件有效,则将其推送到列表中)。

如果此列表是> 0,添加“where”,然后添加“and”加入的列表。

答案 3 :(得分:1)

您可以创建一个if (!empty($day) || !empty($time))变量并像这样检查,而不是像$whereClause这样的检查:

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

如果您需要将某些更改为OR(1 = 1技巧在这种情况下不起作用,甚至可能证明非常危险),这也会有效。

答案 4 :(得分:0)

您可以尝试将变量放在一个数组中,并使用一个布尔值来告诉您是否需要在下一个短语之前添加“AND”。这会将您的控制语句缩短为foreach和嵌套if。

答案 5 :(得分:0)

这是我的解决方案:

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

请注意,由于关闭,这在PHP 5.3下无效。如果您使用较旧的PHP,请将闭包作为单独的函数使用,或者将其替换为foreach

答案 6 :(得分:0)

不幸的是,构建动态SQL是一种乏味的体验,即使你可以在你的逻辑中改变一些东西(实际上看起来相当干净),它仍然会很难看。

幸运的是,Object-relational mapping存在。我不太熟悉PHP,但是Perl有几个CPAN模块,比如SQL :: Abstract,它允许你使用基本数据结构构建相当复杂的SQL语句。

答案 7 :(得分:0)

如果您使用存储过程,则可以执行以下操作:

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

传递null _day表示任何fldDay都可以。 _day的任何其他值,必须匹配。我假设fldDay是文本,但当然你可以在这里正确输入所有内容。

我知道有些人不是存储过程的粉丝,但它可以通过这种方式方便地封装查询逻辑。