如何使用mysqli将变量传递给WHERE子句


How can I pass a variable to a WHERE clause using mysqli?

我目前有一个只有两个元素的小型搜索表单:一个名为search的文本输入和一个名为param的"选择"框。在该框中选择的选项应该用作参数,以选择数据库中的哪个表列将被提取。

使用mysql_函数,可以完成以下操作:

$param  = $_POST['param' ];
$search = $_POST['search'];
$query = 'SELECT * FROM table WHERE $param LIKE "%$search%"';

但是我无法使它与mysqli_语法一起工作。我正在尝试使用准备好的语句,但到目前为止我做得最好的是:

$param  = $_POST['param' ];
$search = $_POST['search'];
if($param == 'first_name')
{
    if($prep = $link->prepare('SELECT * FROM table
                               WHERE first_name
                               LIKE CONCAT("%", ?, "%")'))
    {
        $prep->bind_param('s', $search);
        $prep->execute();
        $prep->bind_result($first_name, $last_name);
        while($prep->fetch())
            echo $first_name . ' ' . $last_name;
        $prep->close();
     }
     else
         echo 'Error while preparing statement.';
}
else if($param == 'last_name')
{
    ...
}

但是,仅仅使用一堆else if似乎是重复和低效的,特别是在我有很多列要处理的情况下。

我尝试的第一件事是参数绑定... WHERE ? LIKE ...$prep->bind_param('ss', $param, $search)-,但它没有起作用(我仍然不知道为什么)。

有没有一种更明智的方法?

如果每个参数都使用相同的SQL代码,只需创建一个可能参数的哈希:(CGI param name => table column name)

$params = array(
    'first_name' => 'first_name',
    'last_name' => 'last_name',
);

从安全角度来看,它要好得多,因为您受到了SQL注入的保护。

然后从散列中获取列名并将其放入查询中,这样就可以去掉if-s:

$name = $params[$param];
$sql = "SELECT * FROM table
WHERE 
$name LIKE ?";
if($prep = $link->prepare($sql))
{
    $prep->bind_param('s', "%$search%");
    ...

正如@Akam所说,在查询中不需要CONCAT("%",?,"%")——最好将值与百分比绑定在一起。

根据PHP手册中的这个例子

http://www.php.net/manual/en/mysqli-stmt.bind-param.php#108790

您似乎最好将"%"添加到要绑定的字符串变量中,而不是在查询中,例如在您的示例中:

if($prep = $link->prepare('SELECT * FROM table
                           WHERE first_name
                           LIKE ?'))
{
    $search='%'.$search.'%';
    $prep->bind_param('s', $search);
    $prep->execute();
    $prep->bind_result($first_name, $last_name);
    while($prep->fetch())
        echo $first_name . ' ' . $last_name;
    $prep->close();
 }

还没有测试过,但这似乎是一个明智的解决方案。

不能为列名使用占位符,因此必须正常连接列名。然而,与其用mysqli来转义它,因为您的列集有限,我建议使用while-list方法:

$allowed_params = array('first_name', 'last_name', etc);
$param  = $_POST['param' ];
$search = $_POST['search'];

if(!in_array($param, $allowed_params))
    die("Uh oh, the request seems to have an invalid param!");
if($prep = $link->prepare('SELECT * FROM table
                           WHERE ' . $param . '
                           LIKE ?'))
{
    $prep->bind_param('s', '%' . $search . '%');
    $prep->execute();
    $prep->bind_result($first_name, $last_name);
    while($prep->fetch())
        echo $first_name . ' ' . $last_name;
    $prep->close();
}
else
    echo 'Error while preparing statement.';

还要注意删除了concat语句,而改为在PHP中进行串联。这对准备好的语句来说很重要,因为一旦它到达服务器,它就不会真正组合它们(从而保护准备好的声明),因此除非通配符与$search字符串一起发送,否则它将无法正常工作。