其他几个答案提到了 SQL 注入的风险,还有几个明确提到使用准备好的语句,但没有一个明确显示你可以如何做到这一点,这对初学者来说可能是一个很大的要求。
我目前解决此问题的首选方法使用 MySQL“IF”语句来检查有问题的参数是否为 null/empty/0(取决于类型)。如果它是空的,那么它将字段值与自身进行比较(WHERE field1=field1
总是返回true
)。如果参数不为空/null/零,则将字段值与参数进行比较。
所以这里有一个使用 MySQLi 准备好的语句的例子(假设 $mysqli 是一个已经实例化的 mysqli 对象):
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = ?
AND location = IF(? = '', location, ?)
AND english_name = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('ssss', $username, $value1, $value1, $value2);
$stmt->execute();
(我假设这$value2
是一个基于字段名称的字符串,尽管 OP 的示例 SQL 中没有引号。)
MySQLi 中无法将同一个参数绑定到语句中的多个占位符,因此我们必须显式绑定$value1
两次。MySQLi 在这种情况下的优势是参数的显式类型 - 如果我们$value1
作为字符串传入,我们知道我们需要将它与空字符串进行比较''
。如果$value1
是整数值,我们可以像这样显式声明:
$stmt->bind_param('siis', $username, $value1, $value1, $value2);
并将其与之进行比较0
。
这是一个使用命名参数的 PDO 示例,因为我认为它们会产生更易读的代码和更少的计数:
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = :user_id
AND location = IF(:location_id = '', location, :location_id)
AND english_name = :name";
$stmt = $pdo->prepare($sql);
$params = [
':user_id' => $username,
':location_id' => $value1,
':name' => $value2
];
$stmt->execute($params);
请注意,使用 PDO 命名参数,我们可以:location_id
在查询中引用多次,而只需绑定一次。