Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate] - php

This question already has answers here:
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Closed 10 months ago.
Imagine we have a query:
SELECT * FROM somewhere WHERE `id` IN(1,5,18,25) ORDER BY `name`;
and an array of IDs to fetch: $ids = array(1,5,18,25)
With prepared statements it's adviced to prepare one statement and call it multiple times:
$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id`=?;');
foreach ($ids as $id){
$stmt->bind_params('i', $id);
$stmt->exec();
}
But now I'll have to sort the results manually. Do I have any nice alternatives?

you could do it this way:
$ids = array(1,5,18,25);
// creates a string containing ?,?,?
$clause = implode(',', array_fill(0, count($ids), '?'));
$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $clause . ') ORDER BY `name`;');
call_user_func_array(array($stmt, 'bind_param'), $ids);
$stmt->execute();
// loop through results
Using this you're calling bind_param for each id and you have sorting done by mysql.

Had the same problem and in addition to the answer of #sled 7 years ago, here is a possibility without making the call_user_func_array(array($stmt, 'bind_param'), $ids); step, but only call bind_params once:
$ids = array(1,5,18,25);
// creates a string containing ?,?,?
$bindClause = implode(',', array_fill(0, count($ids), '?'));
//create a string for the bind param just containing the right amount of s
$bindString = str_repeat('s', count($ids));
$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $bindClause . ') ORDER BY `name`;');
$stmt->bind_param($bindString, ...$ids);
$stmt->execute();

I believe this is the simplest possible answer :
$ids = [1,2,3,4,5];
$pdos = $pdo->prepare("SELECT * FROM somwhere WHERE id IN (:"
. implode(',:', array_keys($ids)) . ") ORDER BY id");
foreach ($ids as $k => $id) {
$pdos->bindValue(":". $k, $id);
}
$pdos->execute();
$results = $pdos->fetchAll();
So long your array of Ids does not contain keys or keys with illegal characters, it wil work.

For the task of executing a secure mysqli query with a dynamic number of incoming values to be fed into the sql string, a prepared statement is the professional technique to implement.
Let's assume that the incoming data payload is user-supplied data -- this means that we cannot guarantee the integrity of the data nor can we guarantee the volume of data. In fact, the expected array of data might be empty. The below snippet will demonstrate how to pass an array of ids to the IN () condition in the WHERE clause of a prepared statement. If there are no values in the array, then a prepared statement provides no benefit and should not be used.
MySQLi result set objects can be immediately iterated by a foreach() loop. Therefore, it is not necessary to make iterated fetch calls; just access the rows' data using array syntax.
The array of ids means that the sql will expect integer values. When calling bind_param(), the first parameter will be a single string of repeated i characters. For general use, if the data will be strings or you might have a mix of data types (e.g. integers, floats/doubles, or strings), then is simpler to just use repeated s characters instead of i characters.
Code: (PHPize.online Demo with SQL)
$ids = [1, 5, 18, 25]; // this could be, for example: $_GET['ids']
$count = count($ids);
$sql = 'SELECT name FROM somewhere';
$orderBy = 'ORDER BY name';
if ($count) {
$placeholders = implode(',', array_fill(0, $count, '?'));
$stmt = $mysqli->prepare("$sql WHERE id IN ($placeholders) $orderBy");
$stmt->bind_param(str_repeat('i', $count), ...$ids);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $mysqli->query("$sql $orderBy"); // a prepared statement is unnecessary
}
foreach ($result as $row) {
echo "<div>{$row['name']}</div>\n";
}
Output from my PHPize demo:
<div>Alan</div>
<div>Bill</div>
<div>Chad</div>
<div>Dave</div>
If you don't need to iterate the result set for any reason, then you can fetch_all(). This is commonly used when immediately echoing or returning a json-encoded string (say, as the response to an ajax call). In this case, you replace the foreach() block with: (PHPize.online Demo with SQL)
echo json_encode($result->fetch_all(MYSQLI_ASSOC));
or simply dump the multidimensional array:
var_export($result->fetch_all(MYSQLI_ASSOC));
Output from my PHPize demo:
[{"name":"Alan"},{"name":"Bill"},{"name":"Chad"},{"name":"Dave"}]
From PHP8.1 and higher, it is no longer necessary to call bind_param() because the execute() method can receive the payload of parameters as an array (like PDO).
This means that...
$stmt->bind_param(str_repeat('i', $count), ...$ids);
$stmt->execute();
can be replaced with...
$stmt->execute($ids);
Here's a complete, basic example: (PHPize.online Demo)
$ids = [1, 2, 3, 4, 5];
$stmt = $mysqli->prepare("SELECT * FROM somewhere WHERE id IN (" . rtrim(str_repeat('?,', count($ids)), ',') . ") ORDER BY id");
$stmt->execute($ids);
var_export($stmt->get_result()->fetch_all(MYSQLI_ASSOC));
Topical Resources:
php.net
The RFC was authored by our very own Dharman ♦ and implemented as part of PHP8.1 after a unanimous vote on 2021-03-27.
phpbackend.com article from 24, October 2021
Reddit thread
PDO can do this concisely.

I'll add a slow & ugly solution which nevertheless uses prepared statements for ANY number of array items :) 3 statements are universal for any case and can be reused everywhere.
CREATE TEMPORARY TABLE `ids`( `id` INT );
INSERT INTO `ids` VALUES(?); this will insert your IDs
SELECT `id` FROM `ids` LEFT JOIN .... ; use data from other tables to sort the ids list
SELECT `id` FROM `ids`; select everything back
Otherwise you'll have to use IN (?,?,?,.... or sort the rows manually. The best idea is to use simple MySQL-queries, or, try to get the list of IDs already sorted in the way you like.

Have you considered rewriting you original query using a JOIN and WHERE clause to get the IDS you need to avoid the need for a WHERE IN clause? I came here with the same question and after reviewing the possible solutions I realized an INNER JOIN was my solution.

Copied from my answer here How to use PDO prepared statements with IN clause?
using named place holders
$values = array(":val1"=>"value1", ":val2"=>"value2", ":val2"=>"$value3");
$statement = 'SELECT * FROM <table> WHERE `column` in(:'.implode(', :',array_keys($values)).') ORDER BY `column`';
using ??
$values = array("value1", "value2", "$value3");
$statement = 'SELECT * FROM <table> WHERE `column` in('.trim(str_repeat(', ?', count($values)), ', ').') ORDER BY `column`';

An alternative would be to use PHP usort function on the result object, but this is "manual."
See this:
Sort Object in PHP

Related

mysql query does not work with where clause and variable [duplicate]

Given an array of ids $galleries = array(1,2,5) I want to have a SQL query that uses the values of the array in its WHERE clause like:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
How can I generate this query string to use with MySQL?
Locked. Comments on this answer have been disabled, but it is still accepting other interactions. Learn more.
BEWARE! This answer contains a severe SQL injection vulnerability. Do NOT use the code samples as presented here, without making sure that any external input is sanitized.
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
Using PDO:[1]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);
Using MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();
Explanation:
Use the SQL IN() operator to check if a value exists in a given list.
In general it looks like this:
expr IN (value,...)
We can build an expression to place inside the () from our array. Note that there must be at least one value inside the parenthesis or MySQL will return an error; this equates to making sure that our input array has at least one value. To help prevent against SQL injection attacks, first generate a ? for each input item to create a parameterized query. Here I assume that the array containing your ids is called $ids:
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
Given an input array of three items $select will look like:
SELECT *
FROM galleries
WHERE id IN (?, ?, ?)
Again note that there is a ? for each item in the input array. Then we'll use PDO or MySQLi to prepare and execute the query as noted above.
Using the IN() operator with strings
It is easy to change between strings and integers because of the bound parameters. For PDO there is no change required; for MySQLi change str_repeat('i', to str_repeat('s', if you need to check strings.
[1]: I've omitted some error checking for brevity. You need to check for the usual errors for each database method (or set your DB driver to throw exceptions).
[2]: Requires PHP 5.6 or higher. Again I've omitted some error checking for brevity.
ints:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
strings:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
Assuming you properly sanitize your inputs beforehand...
$matches = implode(',', $galleries);
Then just adjust your query:
SELECT *
FROM galleries
WHERE id IN ( $matches )
Quote values appropriately depending on your dataset.
Use:
select id from galleries where id in (1, 2, 5);
A simple for each loop will work.
Flavius/AvatarKava's way is better, but make sure that none of the array values contain commas.
As Flavius Stef's answer, you can use intval() to make sure all id are int values:
$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
For MySQLi with an escape function:
$ids = array_map(function($a) use($mysqli) {
return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
}, $ids);
$ids = join(',', $ids);
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");
For PDO with prepared statement:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
We should take care of SQL injection vulnerabilities and an empty condition. I am going to handle both as below.
For a pure numeric array, use the appropriate type conversion viz intval or floatval or doubleval over each element. For string types mysqli_real_escape_string() which may also be applied to numeric values if you wish. MySQL allows numbers as well as date variants as string.
To appropriately escape the values before passing to the query, create a function similar to:
function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}
Such a function would most likely be already available to you in your application, or maybe you've already created one.
Sanitize the string array like:
$values = array_map('escape', $gallaries);
A numeric array can be sanitized using intval or floatval or doubleval instead as suitable:
$values = array_map('intval', $gallaries);
Then finally build the query condition
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
or
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
Since the array can also be empty sometimes, like $galleries = array(); we should therefore note that IN () does not allow for an empty list. One can also use OR instead, but the problem remains. So the above check, count($values), is to ensure the same.
And add it to the final query:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
TIP: If you want to show all records (no filtering) in case of an empty array instead of hiding all rows, simply replace 0 with 1 in the ternary's false part.
Safe way without PDO:
$ids = array_filter(array_unique(array_map('intval', (array)$ids)));
if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
(array)$ids Cast $ids variable to array
array_map Transform all array values into integers
array_unique Remove repeated values
array_filter Remove zero values
implode Join all values to IN selection
Safer.
$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
Col. Shrapnel's SafeMySQL library for PHP provides type-hinted placeholders in its parametrised queries, and includes a couple of convenient placeholders for working with arrays. The ?a placeholder expands out an array to a comma-separated list of escaped strings*.
For example:
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
* Note that since MySQL performs automatic type coercion, it doesn't matter that SafeMySQL will convert the ids above to strings - you'll still get the correct result.
We can use this "WHERE id IN" clause if we filter the input array properly. Something like this:
$galleries = array();
foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}
Like the example below:
$galleryIds = implode(',', $galleries);
I.e. now you should safely use $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
You may have table texts (T_ID (int), T_TEXT (text)) and table test (id (int), var (varchar(255)))
In insert into test values (1, '1,2,3') ; the following will output rows from table texts where T_ID IN (1,2,3):
SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0
This way you can manage a simple n2m database relation without an extra table and using only SQL without the need to use PHP or some other programming language.
More an example:
$galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);
$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'
$statement = $pdo->prepare($sql);
$statement->execute();
Besides using the IN query, you have two options to do so as in an IN query there is a risk of an SQL injection vulnerability. You can use looping to get the exact data you want or you can use the query with OR case
1. SELECT *
FROM galleries WHERE id=1 or id=2 or id=5;
2. $ids = array(1, 2, 5);
foreach ($ids as $id) {
$data[] = SELECT *
FROM galleries WHERE id= $id;
}
Because the original question relates to an array of numbers and I am using an array of strings I couldn't make the given examples work.
I found that each string needed to be encapsulated in single quotes to work with the IN() function.
Here is my solution
foreach($status as $status_a) {
$status_sql[] = '\''.$status_a.'\'';
}
$status = implode(',',$status_sql);
$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
As you can see the first function wraps each array variable in single quotes (\') and then implodes the array.
NOTE: $status does not have single quotes in the SQL statement.
There is probably a nicer way to add the quotes but this works.
Below is the method I have used, using PDO with named placeholders for other data. To overcome SQL injection I am filtering the array to accept only the values that are integers and rejecting all others.
$owner_id = 123;
$galleries = array(1,2,5,'abc');
$good_galleries = array_filter($chapter_arr, 'is_numeric');
$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
"OWNER_ID" => $owner_id,
));
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

Selecting data from an array [duplicate]

This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Closed 11 months ago.
If I have this array:
$ids = [1, 34, 36];
How do I get data from a table from each of ID using this query:
$query = $DB->Prepare('SELECT name, address FROM tContacts WHERE id = ?');
I tried using:
foreach ($ids AS $val) {
$query = $DB->Prepare('SELECT name, address FROM tContacts WHERE id = ?');
$rs = $DB->Execute($query, array($val));
$i = 0;
foreach($rs as $rows)
{
$rs[$i++]=array(
'name' => $rows['name'],
'address' => $rows['address']
);
}
}
And when I tried to:
print_r($rs);
It only display the last row, not 3 rows as I intended.
You should use the IN function in the SQL condition.
Combine it with a php function to repeat the placeholders N times where N is the amount of the requested IDs in the array.
$ids = array(1,434,23);
$query = "SELECT name, address FROM tContacts WHERE id IN('".join("','",$ids)."')";
$stmt = $DB->Prepare($query);
Originally i've used str_repeat, but #pokeybit came up with an idea to use join which is much more comfortable.
In case the $ids array is being built based on a user's input or so, it would be better to use the query prepare mechanism and use placeholders.
$count = count($ids);
$query = "SELECT name, address FROM tContacts WHERE id IN(".str_repeat('?,',$count-1)."?)"; //would output: IN(?,?,?)
Then, use a loop to bind the relevant IDs from the array.
Note 1: Both of the ideas work, one is in case the developer sets the array, the other one use the advantages of preparing queries and binding which is a better solution in case the array's in dynamic/input-based. Therefore, I've wrote both of the solutions.
Note 2: See #JaredFarrish last comment regarding the advantages of prepared queries.

PDO Prepare Not Replacing Named Placeholders

I have tried using named placeholder to fill in the data as shown here:
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN ( :noredo_ids ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->bindParam(':noredo_ids', $_GET["noredo"]);
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute();
As well as trying
$arr2["ids"] = $_GET["noredo"];
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN ( :ids ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute($arr2);
But neither of those are working. But when I try to manually put the string in instead of using placeholders, it does work:
$arr2["ids"] = $_GET["noredo"];
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN (". $arr2['ids'] .") ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute();
Should I manually escape the string? Am I missing something obvious? Thanks!
I am not certain since PHP's documentation on named placeholders for prepared statements is kinda vague about this. http://php.net/manual/en/pdostatement.bindparam.php
But if $_GET['noredo'] is an array of IDs you need to first implode(',',$_GET['noredo']) before passing into the prepared statement, I do not believe that the placeholder replacement is smart enough to flatten the array into a comma separated list acceptable for use in IN().
And in PHP docs, the replacement is a single value and not an array of values, so (and this is where I'm fuzzy) I don't believe that it looks for :named_placeholder in the array you pass it.
But then again, I've only used the ? placeholder for prepared statements...
A query parameter always takes the place of one single scalar value in an SQL expression.
So if you expect $_GET["noredo"] to be an array or a comma-separated list of values, what you're doing won't work. The query will be run as if you did this:
WHERE id NOT IN ( '1,2,3,4' )
A quoted string value that contains a comma-separated list is not the same as a series of comma-separated values. It's one string, and in a numeric context SQL will convert '1,2,3,4' into the scalar number 1.
So you need to use multiple placeholders if you want to bind multiple values in an IN() predicate.
$id_array = (array) $_GET["noredo"];
$placeholders = implode(",", array_fill(0,count($id_array),"?"));
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes
WHERE id NOT IN ( $placeholders ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->execute($id_array);
The second example doesn't work because you used $arr2["ids"] instead of $arr2[":ids"] as far as I can tell. That is, you still have to use the full parameter name.
Also, you should make sure that the values you try to insert are properly sanitized and cannot cause a syntax error.

PDO binding values for MySQL IN statement [duplicate]

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 2 years ago.
I have an issue with PDO that I'd really like to get an answer for after being plagued by it for quite some time.
Take this example:
I am binding an array of ID's to a PDO statement for use in a MySQL IN statement.
The array would be say: $values = array(1,2,3,4,5,6,7,8);
The database-safe variable would be $products = implode(',' $values);
So, $products would then be a STRING with a value of: '1,2,3,4,5,6,7,8'
The statement would look like:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)
Of course, $products would be bound to the statement as :products.
However, when the statement is compiled and values bound, it would actually look like this:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN ('1,2,3,4,5,6,7,8')
The problem is it is executing everything inside of the IN statement as a single string, given that I've prepared it as comma-separated values to bind to the statement.
What I actually need is:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (1,2,3,4,5,6,7,8)
The only way I can actually do this is by placing the values within the string itself without binding them, however I know for certain there has to be an easier way to do this.
This is the same thing as was asked in this question: Can I bind an array to an IN() condition?
The answer there was that, for a variable sized list in the in clause, you'll need to construct the query yourself.
However, you can use the quoted, comma-separated list using find_in_set, though for large data sets, this would have considerable performance impact, since every value in the table has to be cast to a char type.
For example:
select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)
Or, as a third option, you could create a user defined function that splits the comma-separated list for you (cf. http://www.slickdev.com/2008/09/15/mysql-query-real-values-from-delimiter-separated-string-ids/). This is probably the best option of the three, especially if you have a lot of queries that rely on in(...) clauses.
A good way to handle this situation is to use str_pad to place a ? for every value in the SQL query. Then you can pass the array of values (in your case $values) as the argument to execute:
$sql = '
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products.id IN ('.str_pad('',count($values)*2-1,'?,').')
';
$sth = $dbh->prepare($sql);
$sth->execute($values);
$user_ids = $sth->fetchAll();
This way you get more benefit from using prepared statements rather than inserting the values directly into the SQL.
PS - The results will return duplicate user ids if the products with the given ids share user ids. If you only want unique user ids I suggest changing the first line of the query to SELECT DISTINCT users.id
The best prepared statement you could probably come up with in a situation like this is something resembling the following:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)
You would then loop through your values and bind them to the prepared statement making sure that there are the same number of question marks as values you are binding.
you need to provide same number of ?s in IN as the number of values in your $values array
this can be done easily by creating an array of ?s as
$in = join(',', array_fill(0, count($values), '?'));
and use this $in array in your IN clause
THis will dynamically provide you with tailor made array of ?s as per your changiing $values array
You can do so very easily.
If you have an array of values for your IN() statement
EG:
$test = array(1,2,3);
You can simply do
$test = array(1,2,3);
$values = count($test);
$criteria = sprintf("?%s", str_repeat(",?", ($values ? $values-1 : 0)));
//Returns ?,?,?
$sql = sprintf("DELETE FROM table where column NOT IN(%s)", $criteria);
//Returns DELETE FROM table where column NOT IN(?,?,?)
$pdo->sth = prepare($sql);
$pdo->sth->execute($test);
If the expression is based on user input without binding the values with bindValue(), experimental SQL might not be a great choice. But, you can make it safe by checking the syntax of the input with MySQL's REGEXP.
For example:
SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
Here is an example of binding an unknown number of record columns to values for an insert.
public function insert($table, array $data)
{
$sql = "INSERT INTO $table (" . join(',', array_keys($data)) . ') VALUES ('
. str_repeat('?,', count($data) - 1). '?)';
if($sth = $this->db->prepare($sql))
{
$sth->execute(array_values($data));
return $this->db->lastInsertId();
}
}
$id = $db->insert('user', array('name' => 'Bob', 'email' => 'foo#example.com'));
Please try like this:
WHERE products IN(REPLACE(:products, '\'', ''))
Regards

Passing an array to a query using a WHERE clause

Given an array of ids $galleries = array(1,2,5) I want to have a SQL query that uses the values of the array in its WHERE clause like:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
How can I generate this query string to use with MySQL?
Locked. Comments on this answer have been disabled, but it is still accepting other interactions. Learn more.
BEWARE! This answer contains a severe SQL injection vulnerability. Do NOT use the code samples as presented here, without making sure that any external input is sanitized.
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
Using PDO:[1]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);
Using MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();
Explanation:
Use the SQL IN() operator to check if a value exists in a given list.
In general it looks like this:
expr IN (value,...)
We can build an expression to place inside the () from our array. Note that there must be at least one value inside the parenthesis or MySQL will return an error; this equates to making sure that our input array has at least one value. To help prevent against SQL injection attacks, first generate a ? for each input item to create a parameterized query. Here I assume that the array containing your ids is called $ids:
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
Given an input array of three items $select will look like:
SELECT *
FROM galleries
WHERE id IN (?, ?, ?)
Again note that there is a ? for each item in the input array. Then we'll use PDO or MySQLi to prepare and execute the query as noted above.
Using the IN() operator with strings
It is easy to change between strings and integers because of the bound parameters. For PDO there is no change required; for MySQLi change str_repeat('i', to str_repeat('s', if you need to check strings.
[1]: I've omitted some error checking for brevity. You need to check for the usual errors for each database method (or set your DB driver to throw exceptions).
[2]: Requires PHP 5.6 or higher. Again I've omitted some error checking for brevity.
ints:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
strings:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
Assuming you properly sanitize your inputs beforehand...
$matches = implode(',', $galleries);
Then just adjust your query:
SELECT *
FROM galleries
WHERE id IN ( $matches )
Quote values appropriately depending on your dataset.
Use:
select id from galleries where id in (1, 2, 5);
A simple for each loop will work.
Flavius/AvatarKava's way is better, but make sure that none of the array values contain commas.
As Flavius Stef's answer, you can use intval() to make sure all id are int values:
$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
For MySQLi with an escape function:
$ids = array_map(function($a) use($mysqli) {
return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
}, $ids);
$ids = join(',', $ids);
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");
For PDO with prepared statement:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
We should take care of SQL injection vulnerabilities and an empty condition. I am going to handle both as below.
For a pure numeric array, use the appropriate type conversion viz intval or floatval or doubleval over each element. For string types mysqli_real_escape_string() which may also be applied to numeric values if you wish. MySQL allows numbers as well as date variants as string.
To appropriately escape the values before passing to the query, create a function similar to:
function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}
Such a function would most likely be already available to you in your application, or maybe you've already created one.
Sanitize the string array like:
$values = array_map('escape', $gallaries);
A numeric array can be sanitized using intval or floatval or doubleval instead as suitable:
$values = array_map('intval', $gallaries);
Then finally build the query condition
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
or
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
Since the array can also be empty sometimes, like $galleries = array(); we should therefore note that IN () does not allow for an empty list. One can also use OR instead, but the problem remains. So the above check, count($values), is to ensure the same.
And add it to the final query:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
TIP: If you want to show all records (no filtering) in case of an empty array instead of hiding all rows, simply replace 0 with 1 in the ternary's false part.
Safe way without PDO:
$ids = array_filter(array_unique(array_map('intval', (array)$ids)));
if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
(array)$ids Cast $ids variable to array
array_map Transform all array values into integers
array_unique Remove repeated values
array_filter Remove zero values
implode Join all values to IN selection
Safer.
$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
Col. Shrapnel's SafeMySQL library for PHP provides type-hinted placeholders in its parametrised queries, and includes a couple of convenient placeholders for working with arrays. The ?a placeholder expands out an array to a comma-separated list of escaped strings*.
For example:
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
* Note that since MySQL performs automatic type coercion, it doesn't matter that SafeMySQL will convert the ids above to strings - you'll still get the correct result.
We can use this "WHERE id IN" clause if we filter the input array properly. Something like this:
$galleries = array();
foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}
Like the example below:
$galleryIds = implode(',', $galleries);
I.e. now you should safely use $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
You may have table texts (T_ID (int), T_TEXT (text)) and table test (id (int), var (varchar(255)))
In insert into test values (1, '1,2,3') ; the following will output rows from table texts where T_ID IN (1,2,3):
SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0
This way you can manage a simple n2m database relation without an extra table and using only SQL without the need to use PHP or some other programming language.
More an example:
$galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);
$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'
$statement = $pdo->prepare($sql);
$statement->execute();
Besides using the IN query, you have two options to do so as in an IN query there is a risk of an SQL injection vulnerability. You can use looping to get the exact data you want or you can use the query with OR case
1. SELECT *
FROM galleries WHERE id=1 or id=2 or id=5;
2. $ids = array(1, 2, 5);
foreach ($ids as $id) {
$data[] = SELECT *
FROM galleries WHERE id= $id;
}
Because the original question relates to an array of numbers and I am using an array of strings I couldn't make the given examples work.
I found that each string needed to be encapsulated in single quotes to work with the IN() function.
Here is my solution
foreach($status as $status_a) {
$status_sql[] = '\''.$status_a.'\'';
}
$status = implode(',',$status_sql);
$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
As you can see the first function wraps each array variable in single quotes (\') and then implodes the array.
NOTE: $status does not have single quotes in the SQL statement.
There is probably a nicer way to add the quotes but this works.
Below is the method I have used, using PDO with named placeholders for other data. To overcome SQL injection I am filtering the array to accept only the values that are integers and rejecting all others.
$owner_id = 123;
$galleries = array(1,2,5,'abc');
$good_galleries = array_filter($chapter_arr, 'is_numeric');
$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
"OWNER_ID" => $owner_id,
));
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

Categories