mySQL bind_param与IN(?)

时间:2013-02-19 15:03:09

标签: php mysqli

在我的所有查询中使用bind_param,我现在想要使用IN(?),其中列表中的元素数量可能会有所不同。

我在这里使用的SQLout函数主要执行$sql_db->prepare->bind_param->execute()->store_result()->bind_result

// the code below does not work as the query only matches on element 'a':
$locations = ('a','b','c','d','e');

SQLout ("SELECT Name FROM Users WHERE Locations IN (?)",
    array('s', $locations), array(&$usrName));


// the code below does work as a brute-force method,
// but is not a viable solution as I can't anticipate the number of elements in $locations going forward:

SQLout ("SELECT Name FROM Users WHERE Locations IN (?,?,?,?,?)",
    array('sssss', $locations[0],$locations[1],$locations[2],$locations[3],$locations[4]), array(&$usrName));

有没有人想出更优雅的解决方案?

5 个答案:

答案 0 :(得分:5)

这是一个占位符落在他们脸上的地方。减去自动转义,它们几乎只是内部的字符串替换操作,这意味着如果你有WHERE x IN (?)并传入1,2,3,4,你将得到相当于

WHERE x IN ('1,2,3,4')  // note, it's a string, not individual comma-separated ints

在逻辑上等同于

WHERE x = '1,2,3,4' // again, just a string

不是预期的更多

WHERE (x = 1) or (x = 2) or (x = 3) or (x = 4)

唯一可行的解​​决方案是建立自己的?列表,例如:

$placeholders = implode(array_fill(0, count($values), '?'));
$sql = "SELECT ... WHERE x IN ($placeholders)";

然后绑定你的参数通常。

答案 1 :(得分:2)

正如Hazmat所说,你需要建立参数然后通过在准备好的语句上调用call_user_func_array来传递它们,但是比他的例子更接近工作代码:)

//In the calling code
$queryString = "SELECT Name FROM Users WHERE Locations IN (";
$queryString .= getWhereIn($locations);
$queryString .= " )";

$parametersArray = array();

foreach($locations as $location){
    $parameter = array();
    $parameter[0] = 's'; //It's a string
    $parameter[1] = $location;

    $parametersArray[] = $parameter;
}



//This is a function in a class that wraps around the class mysqli_statement
function    bindParameterArray($parameterArray){

    $typesString = '';
    $parameterValuesArray = array();

    foreach($parameterArray as $parameterAndType){
        $typesString .= $parameterAndType[0];
        $parameterValuesArray[] = $parameterAndType[1];
    }

    $finalParamArray = array($typesString);
    $finalParamArray = array_merge($finalParamArray, $parametersArray);
    call_user_func_array(array($this->statement, "bind_param"), $finalParamArray);
}

function getWhereIn($inArray){
    $string = "";
    $separator = "";
    for($x=0 ; $x<count($inArray) ; $x++){
        $string .= $separator."?";
        $separator = ", ";
    }
    return  $string;
}

答案 2 :(得分:1)

在准备/绑定它之前,您可以在IN子句中“构建”。

$sql = 'SELECT Name FROM Users WHERE Locations IN (' . implode(array_fill(0, count($locations), '?')) . ')';

然后你可以使用call_user_func_array绑定参数,而不知道有多少参数。

// Parameters for SQLOut
$params = array(
    # The SQL Query
    $sql,
    # The params for bind_param
    array(str_repeat('s', count($locations))),
    # The params for bind_result
    array(&$usrName)
);

// Add the locations into the parameter list
foreach($locations as &$loc){
    // not sure if this is needed, but bind_param
    // expects its parameters to be references
    $params[1][] = &$loc;
}

// Call your function
call_user_func_array('SQLout', $params);

注意:这是未经测试的

答案 3 :(得分:-1)

IN通常很慢而且没有准备好声明友好。更好的解决方案是构建IN中的项目表,并使用JOIN来获得相同的效果。

答案 4 :(得分:-2)

  

有没有人想出更优雅的解决方案?

不确定。我是。

Mysqli几乎无法使用准备好的陈述,特别是对于这种复杂的案件 因此,最好摆脱准备好的陈述并实施自己的占位符,并支持开发人员可以遇到的所有现实案例

所以,safeMysql有一个你正在寻找的解决方案(以及其他十几种令人头痛的解决方案)。

在您的特定情况下,它将像这一行代码一样简单

// the code works all right:
$locations=array('a','b','c','d','e');
$usrName=$db->getCol("SELECT Name FROM Users WHERE Locations IN (?a)",$locations);

与丑陋的代码不同,您可以使用一些PHP和API函数(并且仍然会受到令人不安的警告取决于您目前使用的PHP版本)此代码整洁且可读。这是重要的事情。你可以告诉这段代码在一年过去后会做些什么。