This question already has answers here:
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Closed 3 years ago.
I’m moving some old code over to the new msqli interface using prepared statements, I’m having trouble with SQL statements containing the IN clause. I would just normally do this:
$ids = '123,535,345,567,878'
$sql = "SELECT * FROM table WHERE id IN ($ids)";
$res = mysql_query($sql);
Converting this to mysqli and prepared statements I have tried a number of solutions:
$ids = '123,535,345,567,878'
$ids = implode($ids,',');
$result = $msqli->prepare("SELECT foo,blar FROM table WHERE id IN (?));
$result->bind_param("i", $ids);
$result->execute();
The above fails and calculating the number of elements in the array and altering number of question marks in the SQL string and calling bind_parm for each element in the array also fails. Just using the comma separated string also fails.
I can find no good documentation in Google on this, so how have you solved the problem?
It's not possible to bind a list of variable length to a single bound variable.
Similarly, if you were to bind the string $ids you'll actually end up with:
SELECT foo,blar FROM table WHERE id IN ('123,535,345,567,878')
(Note the quotes around the list of IDs).
Creating your own query with the right number of question marks and bound parameters should have actually worked - you may need to try that again and report on the actual error.
Alternatively, this may be one of those occasions where it's unfortunately necessary to hand-craft your own SQL and not use bound parameters.
Look at the answer to a similar question that has been asked here before (second code sample):
I have an array of integers, how do I use each one in a mysql query (in php)?
It boils down to:
create the SQL string with the right amount of question marks
use call_user_func_array() to bind your array to the query string
I thought the point of prepared statements was so in this situation you could just do:
$stmt = $this->mysqli->prepare("UPDATE radcheck SET attribute = ?, value = ? WHERE username = ? AND attribute LIKE 'CS-Total-Octets%'");
foreach ($usernames as $username)
{
$stmt->bind_param('sss', $bandwidth_types[$bandwidth_type], $bandwidth_bytes, $username);
$stmt->execute();
}
$stmt->close();
Related
This question already has answers here:
PHP - Using PDO with IN clause array
(9 answers)
MySQLi prepared statements with IN operator [duplicate]
(1 answer)
Closed 3 years ago.
So I have this sql preparted statment:
SELECT carName, modelName
FROM cars
INNER JOIN model ON cars.idCar = model.idCar
WHERE carName IN (?)`
$input = " 'toyota','honda' "
I am trying to put this into the sql statment, but it gives zero rows out. I have tried the query in phpMyAdmin and there it works all fine. Anyone know the problem?
You need one parameter per value in the IN list. If you pass a single parameter, it is interpreted as a unique string that contains a comma, which is not what you want (and you end up with no match, since none of the names in the table matches this value).
So for two parameters:
SELECT carName, modelName
FROM cars
INNER JOIN model ON cars.idCar = model.idCar WHERE carName IN (?, ?);
The IN clause takes multiple arguments and each parameter can only take on one value. So to automate the process of inserting multiple parameters in this statement you could use something like the following:
/* Execute a prepared statement using an array of values for an IN clause */
$params = ['toyota', 'honda'];
/* Create a string for the parameter placeholders filled to the number of params */
$place_holders = implode(',', array_fill(0, count($params), '?'));
/*
This prepares the statement with enough unnamed placeholders for every value
in our $params array. The values of the $params array are then bound to the
placeholders in the prepared statement when the statement is executed.
This is not the same thing as using PDOStatement::bindParam() since this
requires a reference to the variable. PDOStatement::execute() only binds
by value instead.
*/
$st = $db->prepare("SELECT carName, modelName FROM cars WHERE carName IN ($place_holders)");
$st->execute($params);
This question already has answers here:
How do I set ORDER BY params using prepared PDO statement?
(7 answers)
Closed 5 years ago.
I am using different values in ORDER BY clause of SQL queries based upon user selection. How do I escape this selected value using mysqli_real_escape_string() function?
For example, the url is as following:
localhost/person/person_listing.php?sort_by=date_of_birth
Based on this I am using:
if (isset($_GET['sort_by'])) {
$sort_by = trim($_GET['sort_by']);
if (!empty($sort_by)) {
$order_by_sql = " ORDER BY $sort_by";
}
}
The question is, what is the best way to escape this type of add-on to SQL? Can the entire ORDER BY clause be escaped at once, or each value has to be escaped individually?
The best way to do this would be to use a prepared statement. Your code would look kind of as follows: (grabbed from here.
Basically, you add a question mark wherever you have a variable you would want to pass. And then you pass it with the mysqli_stmt_bind_param function. ss here means that you want to pass 2 strings.
if ($stmt = mysqli_prepare($link, "SELECT * FROM users WHERE Name=? ORDER BY ?")) {
/* bind parameters for markers */
mysqli_stmt_bind_param($stmt, "ss", $name, $sort_by);
}
This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Build SELECT query with dynamic number of LIKE conditions as a mysqli prepared statement
(2 answers)
Closed 11 months ago.
I'm currently facing a difficulty where putting a comma-separated values to a MySQL NOT IN doesn't give me the result I was hoping for. There must be something I'm missing as I'm unsure what to search for this particular problem. Running only the MySQL code works, but passing the parameter from another PHP function didn't.
Here's the code that's giving me a problem:
$uid = 1;
$selected_uids = '1,2';
$result = $db->retrieveFollowingWithCondition($uid, $selected_uids);
...then somewhere along the code...
public function retrieveFollowingWithCondition($uid, $selected_uids) {
$stmt = $this->conn->prepare("SELECT *
FROM `friendlist`
WHERE `uid` = ? AND `buddy_uid` NOT IN (?)
GROUP BY `buddy_uid`;");
$stmt->bind_param("is", $uid, $selected_uids);
...}
I've tested just putting '2' in $selected_uids and it actually works. But once there's comma involved, the code runs but the $selected_uids are still in the result. Not sure this is a bad practice or just needing a minor adjustment to the code. Anyway, I'm really looking forward to understand why it's not working for me.
By using s in bind_param you are telling PHP to treat the entire contents of $selected_uids as a string. Therefore, "1,2" is treated as ('1,2') instead of (1,2). Your problem is that bind_param doesn't support arrays, so support of IN queries is limited. There are a number of alternatives to get around this limitation, but since you are dealing with a list of ints, I would probably do a raw string concat.
// using is_numeric because is_int("1") === false
$filtered = array_filter('is_numeric', $selected_uids);
// You could also just call array_map('intval', $selected_uids);
// Depending on your needs.
if(!$filtered) {
return; // No valid values
}
$filteredStr = implode(',', $filtered);
$stmt = $this->conn->prepare("SELECT *
FROM `friendlist`
WHERE `uid` = ? AND `buddy_uid` NOT IN ($filteredStr)
GROUP BY `buddy_uid`;");
Should also be noted: if I were trying to use strings for an IN query, I would likely do the following:
$filtered = array_map([$this->conn, 'escape_string'], $queried);
$inQuery = '\'' . implode('\',\'', $filtered) . '\'';
I find that notation cleaner and easier than a dynamically generated bind_param format string.
You should bind every parameter in IN(...) separately, but method bind_param doesn't support multiple calls. There is a nice class that can do this and you can find it on PHP documentation pages:
Custom class for multiple bind_param
This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 7 years ago.
Is it possible to use prepared statements with either MySQLi or PDO and still be able to dynamically add items to the IN part of the query, for example...
$somearray = ['tagvalue1', 'tagvalue2', 'tagvalue3'];
$sql = "SELECT foo FROM bar
WHERE tag IN(?)";
I ask this because I have a situation whereby the number of elements in the IN part is not known until runtime.
You asked:
Is it possible to use prepared statements with either MySQLi or PDO
and still be able to dynamically add items to the IN part of the
query, for example...
No, unfortunately it is not. It happens that ColdFusion does this, but not php.
While you can't do exactly what you want with a prepared query, you can dynamically generate the $sql string for the query to accomplish the same thing.
Given some array $array = (n, n1, n2, ... nN)
$sql = "SELECT foo FROM bar WHERE tag IN (";
foreach($array as $value) {
$sql .= "'" . $value . "', ";
}
// Strip off the last comma and space from the IN clause
$sql = substr($sql, 0, strlen($sql) - 2);
$sql .= ")";
It's certainly not the most elegant solution and you'll have to do some more data validation or escaping of dangerous characters that a prepared query would handle better, but it will do the job.
As a side note, there are ORM (Object Relational Mapper) libraries that support things like accepting an array of values to generate the IN clause in a database statement. Propel is the one I have most experience with, but I'm sure others like Doctrine would have a similar method.
A propel-ish example would be like
$results = BarQuery::create()
->select('foo')
->filterByTag(array($value1, $value2, ..., $vauleN)
->find();
Lots of added functionality and support, but does increase your initial set up time for a project.
I have a query that looks like this:
SELECT CONCAT('path/to/page/?id=', id) AS link FROM users WHERE name = ?
I am using PDO to prepare this statement and I am getting the error
Invalid parameter number: number of bound variables does not match number of tokens
because it thinks the question mark in the CONCAT string is a placeholder.
Is there any way to escape the question mark so PDO knows that it is not a placeholder?
Please no comments about other ways to get the link. I am changing old code that goes into an old templating engine so it would be A LOT less work to find a way to escape the question mark than to not put a question mark in the query.
PDO is not confused by the question mark inside the quotes. I just test this with PHP 5.5.15.
$sql = "SELECT CONCAT('path/to/page/?id=', id) AS link FROM foo WHERE name = ?;";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, 'name');
$stmt->execute();
print_r($stmt->fetchAll());
It works fine, with no error about a wrong number of parameters. Your error is caused by the way you're binding parameters, not by the SQL syntax.
I suspect you haven't shown us the whole SQL query, because WHERE without FROM is a syntax error anyway. So you must have additional parameter placeholders that you haven't shown us. It would also be helpful if you show us the way you're binding parameters (or passing parameters to execute()).
This is a very old question, but the escaping of question marks have landed into PHP 7.4.
A question mark is escaped by adding a second... question mark.
In your query, you could add this:
$sql = "SELECT CONCAT('path/to/page/??id=', id) AS link FROM users WHERE name = ?";
For usage with json or jsonb operator in postgresql:
$sql = "SELECT '["a", "b", "c"]'::jsonb ?? 'a'";