2013-02-19 80 views
5

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

我在这里使用基本上都一个 $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)); 

有没有人想出了一个更优雅的解决这个

的SQLOUT功能?

+0

[PHP PDO的可能重复稍微接近工作代码:我可以将数组绑定到IN()条件吗?](http://stackoverflow.com/questions/920353/php-pdo-can-i-bind-an-array-to-an-in-condition) – 2013-02-19 15:07:37

+1

PDO的答案与mysqli问题无关。在处理可变数量的准备语句方面,PDO与mysqli相比是一个天堂 – 2013-02-19 15:22:04

回答

4

这是一个地方的占位符落在他们的脸上。减去自动转义,他们几乎真的只是一个字符串替换operationt内部,也就是说如果你有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) 

唯一可行的解​​决方案的isntead是建立自己的名单,例如:

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

然后绑定你的参数是平常的。

+0

'$ stmt-> execute($ values)'。尽可能简单。 – 2013-02-19 15:26:39

+2

不幸的是,在mysqli中绑定准备好的语句并不像在PDO中那么容易。 Mysqli没有'$ stmt-> execute($ values)'功能。它也没有bindValue方法(所以你不能在一个使用迭代变量的循环中绑定),这使得这样一个简单的任务成为马戏团和噩梦。这就是为什么这个答案是不完整的 – 2013-02-19 16:09:35

+0

最适合我的情况。我将不得不运行一些使用长IN元素的速度测试。 – user2033684 2013-02-19 16:32:51

-1

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

+0

@您接着提供一个如何在IN中获取可变数量值的示例。 – 2013-02-19 15:07:51

+0

@你请阅读http://stackoverflow.com/questions/4771183/sql-fixed-value-in-vs-inner-join-performance与IN的问题是,你不能索引列表,你可以在一个表和列表越来越长,性能迅速下降。当列表很短时,它当然很快。 – 2013-02-19 15:15:42

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

注:这是未经测试

-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版本你现在使用的)这个代码是整洁的,并且可读。这是重要的事情。即使经过了一年,你也可以知道这些代码的作用。

+0

如果列表很长,它仍然会很慢http://stackoverflow.com/questions/4771183/sql-fixed-value-in-vs-inner-join-performance – 2013-02-19 15:21:16

2

由于危险品说,你需要建立的参数,然后通过在准备好的声明,呼吁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; 
}