Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - Using PDO with IN clause array

I'm using PDO to execute a statement with an IN clause that uses an array for its values:

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

The above code works perfectly fine, but my question is why this doesn't:
 $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();

This code will return the item whose my_value equals the first item in the $in_array (1), but not the remaining items in the array (2, and 3).

like image 224
iRector Avatar asked Feb 08 '13 07:02

iRector


People also ask

Is PHP PDO deprecated?

PDO (PHP Data Objects) However, this extension was deprecated in 2012.

Does PHP 5.4 support PDO?

PDO will not run since upgrading PHP to 5.4.

How fetch data from database in PHP and display PDO?

Fetch data from a result set by calling one of the following fetch methods: To return a single row from a result set as an array or object, call the PDOStatement::fetch method. To return all of the rows from the result set as an array of arrays or objects, call the PDOStatement::fetchAll method.

What function do you use to run a query using a PDO object?

To execute an SQL statement that returns one or more result sets, call the PDO::query method on the PDO connection object, passing in a string that contains the SQL statement. For example, you might want to call this method to execute a static SELECT statement.


5 Answers

PDO is not good with such things. You need to create a string with placeholders dynamically and insert it into the query, while binding array values the usual way. With positional placeholders it would be like this:

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

In case there are other placeholders in the query, you could use the following approach (the code is taken from my PDO tutorial):

You could use array_merge() function to join all the variables into a single array, adding your other variables in the form of arrays, in the order they appear in your query:

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

In case you are using named placeholders, the code would be a little more complex, as you have to create a sequence of the named placeholders, e.g. :id0,:id1,:id2. So the code would be:

// other parameters that are going into query
$params = ["foo" => "foo", "bar" => "bar"];

$ids = [1,2,3];
$in = "";
$i = 0; // we are using an external counter 
        // because the actual array keys could be dangerous
foreach ($ids as $item)
{
    $key = ":id".$i++;
    $in .= ($in ? "," : "") . $key; // :id0,:id1,:id2
    $in_params[$key] = $item; // collecting values into a key-value array
}

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

Luckily, for the named placeholders we don't have to follow the strict order, so we can merge our arrays in any order.

like image 69
Your Common Sense Avatar answered Oct 22 '22 13:10

Your Common Sense


Variable substitution in PDO prepared statements doesn't support arrays. It's one for one.

You can get around that problem by generating the number of placeholders you need based on the length of the array.

$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)) {
    // ...
}
like image 20
GordonM Avatar answered Oct 22 '22 13:10

GordonM


As PDO doesn't seem to provide a good solution, you might as well consider using DBAL, which mostly follows PDO's API, but also adds some useful features http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion

$stmt = $conn->executeQuery('SELECT * FROM articles WHERE id IN (?)',
    array(array(1, 2, 3, 4, 5, 6)),
    array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
);

There are probably some other packages out there that don't add complexity and don't obscure the interaction with the database (like most ORM do), but at the same time make small typical tasks bit easier.

like image 44
Dienow Avatar answered Oct 22 '22 13:10

Dienow


An alternative version of PHP Delusions (@your-common-sense) using closures:

$filter      = ["min_price" => "1.98"];
$editions    = [1,2,10];

$editions = array_combine(
    array_map(function($i){ return ':id'.$i; }, array_keys($editions)),
    $editions
);
$in_placeholders = implode(',', array_keys($editions));
$sql = "SELECT * FROM books WHERE price >= :min_price AND edition IN ($in_placeholders)";
$stm = $pdo->prepare($sql);
$stm->execute(array_merge($filter,$editions));
$data = $stm->fetchAll();
like image 3
celsowm Avatar answered Oct 22 '22 13:10

celsowm


I often use FIND_IN_SET instead of IN, like this:

$in_array = array(1, 2, 3);
$in_values = implode(',', $in_array);
$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE FIND_IN_SET(my_value, :in_values)");
$my_result->execute(array(':in_values' => $in_values));
$my_results = $my_result->fetchAll();

It is not the best solution performance wise, but if the possible number options of the $in_array are limited than it is usually not an issue. I use it often for statuses where my_value is an enum field. Never had any issue with it.

like image 2
Ted Avatar answered Oct 22 '22 13:10

Ted