PHP – 与IN子句数组一起使用PDO
我正在使用PDO执行一个IN
语句的语句,该语句使用数组作为其值:
$in_array = array(1, 2, 3); $in_values = implode(',', $in_array); $my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (".$in_values.")"); $my_result->execute(); $my_results = $my_result->fetchAll();
上面的代码工作得很好,但我的问题是为什么这不:
$in_array = array(1, 2, 3); $in_values = implode(',', $in_array); $my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (:in_values)"); $my_result->execute(array(':in_values' => $in_values)); $my_results = $my_result->fetchAll();
此代码将返回my_value
等于$in_array
(1)中的第一项的项目,但不返回数组中剩余的项目(2和3)。
PDO对这样的事情不好。 您需要dynamic创build带有问号的string并插入到查询中。
$in = str_repeat('?,', count($in_array) - 1) . '?'; $sql = "SELECT * FROM my_table WHERE my_value IN ($in)"; $stm = $db->prepare($sql); $stm->execute($in_array); $data = $stm->fetchAll();
如果查询中有其他占位符,可以使用以下方法(代码取自我的PDO教程 ):
您可以使用array_merge()
函数将所有variables连接到一个数组中,按照它们在查询中出现的顺序,以数组的forms添加其他variables:
$arr = [1,2,3]; $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?"; $stm = $db->prepare($sql); $params = array_merge([$foo], $arr, [$bar, $baz]); $stm->execute($params); $data = $stm->fetchAll();
如果您使用的是指定的占位符,则代码会稍微复杂一些,因为您必须创build一系列指定的占位符,例如:id0,:id1,:id2
。 所以代码将是:
// other parameters that are going into query $params = ["foo" => "foo", "bar" => "bar"]; $ids = [1,2,3]; $in = ""; foreach ($ids as $i => $item) { $key = ":id".$i; $in .= "$key,"; $in_params[$key] = $item; // collecting values into key-value array } $in = rtrim($in,","); // :id0,:id1,:id2 $sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar"; $stm = $db->prepare($sql); $stm->execute(array_merge($params,$in_params)); // just merge two arrays $data = $stm->fetchAll();
幸运的是,对于指定的占位符,我们不必遵循严格的顺序,所以我们可以按任意顺序合并我们的数组。
PDO准备语句中的variablesreplace不支持数组。 这是一个一个。
您可以通过根据数组长度生成所需的占位符数来解决该问题。
$variables = array ('1', '2', '3'); $placeholders = str_repeat ('?, ', count ($variables) - 1) . '?'; $query = $pdo -> prepare ("SELECT * FROM table WHERE column IN($placeholders)"); if ($query -> execute ($variables)) { // ... }
据我所知,这是因为PDO将$ in_values内容视为一个单一的项目,并会相应地相当。 PDO将1,2,3看作是一个单独的string,所以查询看起来像这样
SELECT * FROM table WHERE my_value IN(“1,2,3”)
你可能会认为改变内部引号和逗号会解决它,但不会。 PDO将会看到引号并改变引用string的方式。
至于为什么你的查询匹配的第一个值,我没有任何解释。
我刚刚提出了这个问题,并编写了一个小包装。 这不是我确定的最漂亮或最好的代码,但它可能有助于这样的人:
function runQuery(PDO $PDO, string $sql, array $params = []) { if (!count($params)) { return $PDO->query($sql); } foreach ($params as $key => $values) { if (is_array($values)) { // get placeholder from array, eg ids => [7,12,3] would be ':ids' $oldPlaceholder = ':'.$key; $newPlaceholders = ''; $newParams = []; // loop through array to create new placeholders & new named parameters for($i = 1; $i <= count($values); $i++) { // this gives us :ids1, :ids2, :ids3 etc $newKey = $oldPlaceholder.$i; $newPlaceholders .= $newKey.', '; // this builds an associative array of the new named parameters $newParams[$newKey] = $values[$i - 1]; } //trim off the trailing comma and space $newPlaceholders = rtrim($newPlaceholders, ', '); // remove the old parameter unset($params[$key]); // and replace with the new ones $params = array_merge($params, $newParams); // amend the query $sql = str_replace($oldPlaceholder, $newPlaceholders, $sql); } } $statement = $PDO->prepare($sql); $statement->execute($params); return $statement; }
例如,通过这些:
SELECT * FROM users WHERE userId IN (:ids) array(1) { ["ids"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } }
变为:
SELECT * FROM users WHERE userId IN (:ids1, :ids2, :ids3) array(3) { [":ids1"]=> int(1) [":ids2"]=> int(2) [":ids3"]=> int(3) }
这不是防弹的,但作为我需要的唯一开发者,它到目前为止还是不错的。