This question already has answers here:
PHP - Using PDO with IN clause array
(9 answers)
Closed 9 months ago.
My code:
$myArray = implode($myArray, ',');
$sth = $dbh->prepare('SELECT foo FROM bar WHERE ids IN (:ids)');
$sth->bindParam(':ids', $myArray);
$sth->execute();
$result = $sth->fetch();
echo $sth->rowCount();
Always shows a count of 1, but when I skip the parametrization and just add the variable itself in it's place, I get an accurate count. What's going on here?
You can't bind a parameter for the IN clause like that. The $myArray string will only count as one value, like if you did this:
SELECT foo FROM bar WHERE ids IN ('1,2,3')
Even though there are three comma delimited values, the database reads them as only one string value.
You need to manually insert the IN list into the query, the old-school way.
'SELECT foo FROM bar WHERE ids IN (' . $myArray .')'
There is unfortunately no other way. At least for now.
I know this question is old, but this works for me:
$arrayOfValues = array(1,2,3,4,5);
$questionMarks = join(",", array_pad(array(), count($arrayOfValues), "?"));
$stmt = $dbh->prepare("update some_table set end_date = today where value_no in ($questionMarks)");
$stmt->execute($arrayOfValues);
You simply need a string with as many question marks as you have parameters for your in clause... easily possible
<?php
$sql = "select * from something where id in (".implode(",", array_fill(0, count($parameters), "?")).")";
$sth = $db->prepare($sql);
$sth->execute( $parameters);
?>
This question already has answers here:
PHP - Using PDO with IN clause array
(9 answers)
Closed 9 months ago.
I want to bind a array of Strings to the WHERE IN part of a SQL command, which I want to run afterwards on a SQL Server. The problem is probably that I try to bind an array of Strings and not an array of integers.
$totalCount =
"SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE referral IN ($refIdsPartial) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row){
echo "Test<br>";
}
I echoed $refIdsPartial for you, so you have an idea what this is:
54469c27c687b332339627,54469ba0dec3e703865612,54469c77945c7091266617
Its just an imploded array of strings/varchars.
I tested the SQL command with my Managementstudio and I can ensure that this SQL command works, as long das I use the quote signs for each String/Varchar. Example:
SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE referral IN ('54469c27c687b332339627','54469ba0dec3e703865612') GROUP BY referral
My problem:
In the code above it never goes into the foreach, so the result of the Query seems to be empty. What is wrong there (Ofcourse I tested only queries which should have results)?
You could use some string manipulation.
You can count the number of ? you'd need by using str_repeat("?", count(explode(",", $refIdsPartial))). This will create your placeholders.
$totalCount =
"SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE referral IN (". str_repeat("?,", count(explode(",", $refIdsPartial))-1) . "?) GROUP BY referral";
Now that the placeholders are in place, you can explode the , from the string and execute
$ps_totalCounts->execute( explode(",", $refIdsPartial) );
Here is the snippet I use when trying to achieve an IN statement with an array.
This works dynamically, so whether you have an array of 2 or 200 it should execute as expected.
$ids = array(1,2,3);
$in = str_repeat('?,', count($ids) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($ids);
$data = $stm->fetchAll();
Your code will look like so:
$refIdsPartial = array('54469c27c687b332339627','54469ba0dec3e703865612','54469c77945c7091266617');
$in = str_repeat('?,', count($refIdsPartial ) - 1) . '?';
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($in) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row)
{
echo "Test<br>";
}
I faced similar problem with quite a big array to bind. Instead of skipping binding and injecting whole array directly to query or making workarounds with dynamically generating multiple unique placeholders to bind each record of array, I went for using find_in_set mysql function. Read more here.
In your case it would be:
$totalCount =
"SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE find_in_set(referral,$refIdsPartial) GROUP BY referral";
This question already has answers here:
Can PHP PDO Statements accept the table or column name as parameter?
(8 answers)
Closed 9 years ago.
I want to fetch results from a MySQL database with PDO. The user should be able to order them by tablerow by which type (ascending or descending). This seems to only work when you hardcode it.
Does work:
$query = "SELECT * FROM ".$config->dbPrefix."content
WHERE cat_id = 2
ORDER BY id DESC
";
$query = $pdo->prepare($query);
$query->execute();
$result = $query->fetchAll();
Doesn't work:
$orderRow = 'id'; //from $_POST
$orderType = 'DESC' //from $_POST
$query = "SELECT * FROM ".$config->dbPrefix."content
WHERE cat_id = 2
ORDER BY :orderRow :orderType
";
$query = $pdo->prepare($query);
$query->bindValue(':orderRow', $orderRow);
$query->bindValue(':orderType', $orderType);
$query->execute();
$result = $query->fetchAll();
So my question is: what is the best way to do this and why isn't this implemented?
The best way I can think of is using a switch statement and writing the query for every different option which would have like 14 different available cases.
You can only provide placeholders for values in an SQL statement, not for column names or other kind of identifiers.
So instead of using bindValue, put the values in like you do for #config->dbPrefix, directly into the string. Make sure however that no SQL injection is possible.
This question already has an answer here:
Can I use a PDO prepared statement to bind an identifier (a table or field name) or a syntax keyword?
(1 answer)
Closed 3 years ago.
Is it possible pass a column name as parameter in a prepared MySQL statement? Take the following example:
UPDATE Images
SET :placement = :imageURL
WHERE ID = :titleID;
PDO adds ' around each parameter, so the middle line above becomes:
SET 'Homepage' = '1.jpg'
Which MySQL doesn't like. Is there a way to include parameters for fieldnames in PDO statements and have them accepted?
Otherwise I guess I'll have to write several different PDO statements, depending on what's been chosen(?).
You would need to do something like this:
$column = 'someColumn';
$stmt = $db->prepare("UPDATE tableName SET {$column} = :columnValue WHERE ID = :recordId");
Parameterized placeholders are only for values.
I would suggest you read the comment #YourCommonSense posted on your question.
In situations such as this, I use a different sort of replacement parameters, like so:
$unitLabel = 'store_number';
$sql = 'select * from users where [unitLabel] = :unit and level = :level;';
$sql = str_replace('[unitLabel]', $unitLabel, $sql);
$params = array(
':unit' => 300,
':level' => 'admin',
);
$stmt = $dbh->prepare($sql);
$stmt->execute($params);
The prepared SQL query ends up being processed (more or less) as:
SELECT * FROM USERS WHERE store_number = 300 AND level = 'admin';
Which works for my situation. I hope this helps. :)
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