MYSQL - PDO with multiple IN clause - php

I can successfully implement a IN clause within a PDO prepared statement using the following code.
in_array = array(1,2,3);
$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();
How can I do the same for multiple $in? For example I've unsuccessfully tried the following:
in_array1 = array(1,2,3);
$in1 = str_repeat('?,', count($in_array) - 1) . '?';
in_array2 = array(4,5,1);
$in2 = str_repeat('?,', count($in_array) - 1) . '?';
$sql = "SELECT * FROM my_table WHERE (my_value1 IN ($in1)) AND (my_value2 IN ($in2))";
$stm = $db->prepare($sql);
$stm->execute($in_array1,$in_array2);
$data = $stm->fetchAll();
I think its got to do with stm->execute but not sure, help appreciated

Your current query comes out as
SELECT * FROM my_table WHERE (my_value1 IN (?,?,?)) AND (my_value2 IN (?,?,?))
So your execute usage is incorrect, http://php.net/manual/en/pdostatement.execute.php. It should only be passing one array with values inside it.
An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.
I think using array_merge, http://php.net/manual/en/function.array-merge.php, will allow you to accomplish what you are trying
$stm->execute(array_merge($in_array1,$in_array2));
This way the execute is the equivalent of
$stm->execute(array(1,2,3,4,5,1));
This may seem incorrect because the array pairings are now gone but placeholder 1 (the first question mark) will map to 1, placeholder 4 to 4, and so on.

Doesn't make sense.
Look at this:
$query = $db->prepare("SELECT * FROM table WHERE value1 = ? AND value2 = ?");
$query ->execute(array($value1, $value2));
and this:
http://php.net/manual/en/pdo.prepare.php

Related

PDO: Bind an array to an IN() condition with multiple WHERE clauses?

Based on Can I bind an array to an IN() condition?
The above question doesn't have any answers for using multiple WHERE clauses.
The second best answer works only with one WHERE clause:
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
If I put an additional WHERE clause, it produces an empty result or error.
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks) AND locationid=?");
$sth->execute(array($ids, ?)); // Also tried $sth->execute($ids, $location_id) and $sth->execute($ids, array($location_id));
Please advise.
execute accepts a single array of parameters. You need to do:
$sth->execute(array_merge($ids, [$location_id]));

Prepared statement WHERE IN clause behaving unexpected [duplicate]

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 1 year ago.
I'm reworking some PHP code to use PDO for the database access, but I'm running into a problem with a "WHERE... IN" query.
I'm trying to delete some things from a database, based on which items on a form are checked. The length and content of the list will vary, but for this example, imagine that it's this:
$idlist = '260,201,221,216,217,169,210,212,213';
Then the query looks like this:
$query = "DELETE from `foo` WHERE `id` IN (:idlist)";
$st = $db->prepare($query);
$st->execute(array(':idlist' => $idlist));
When I do this, only the first ID is deleted. (I assume it throws out the comma and everything after it.)
I've also tried making $idlist an array, but then it doesn't delete anything.
What's the proper way to use a list of items in a PDO prepared statement?
Since you can't mix Values (the Numbers) with control flow logic (the commas) with prepared statements you need one placeholder per Value.
$idlist = array('260','201','221','216','217','169','210','212','213');
$questionmarks = str_repeat("?,", count($idlist)-1) . "?";
$stmt = $dbh->prepare("DELETE FROM `foo` WHERE `id` IN ($questionmarks)");
and loop to bind the parameters.
This may be helpful too:
https://phpdelusions.net/pdo#in
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($arr);
$data = $stm->fetchAll();
I would make $idlist and array, then simply loop through the array using foreach to delete the specific item.
$idlist = array('260','201','221','216','217','169','210','212','213');
$stmt = $dbh->prepare("DELETE FROM `foo` WHERE `id` = ?");
$stmt->bindParam(1, $id);
foreach ($idlist as $item){
$id = $item;
$stmt->execute();
}

MySQL WHERE IN () + AND , PDO returns only one row

following query returns all wanted results if entered in phpmyadmin:
SELECT postid, voting
FROM postvotes
WHERE userid = 1
AND postid IN
(1007,1011,1012,1013,1014,
1015,1016,1017,1018,1019,1020,1021,1023,1025,1026,
1027,1028,1029,1030,1031)
But PDO fails to fetchAll(). It just returns the first match like fetch().
What's wrong?
PHP Code:
private function userPostVotings( $postIDs ) {
// $postIDs contains a string like 1,2,3,4,5,6,7...
// generated through implode(',', idArray)
try {
$userPostVote = $this->_db->prepare('SELECT postid, voting
FROM postvotes
WHERE userid = ?
AND postid IN ( ? )');
$userPostVote->setFetchMode(\PDO::FETCH_ASSOC);
$userPostVote->execute( array( $this->_requester['id'], $postIDs ) );
while ( $res = $userPostVote->fetch() ) {
var_dump( $res );
}
} catch (\PDOException $p) {}
}
If I echo out the query used in this method and fire it through phpmyadmin I get the correct number of results. However PDO gives just the first. No matter if a loop with fetch() or fetchAll().
You cannot bind array in prepared statements in PDO.
Reference:
Can I bind an array to an IN() condition?
it is not PDO's fetchAll() of course, but your query.
Which is not
IN (1007,1011,1012,1013,1014)
but
IN ('1007,1011,1012,1013,1014')
and of course it will find only first value as this string will be cast to the first number
One have to create a query with placeholders representing every array member, and then bind this array values for execution:
$ids = array(1,2,3);
$stm = $pdo->prepare("SELECT * FROM t WHERE id IN (?,?,?)");
$stm->execute($ids);
To make this query more flexible, it's better to create a string with ?s dynamically:
$ids = array(1,2,3);
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($ids);
$data = $stm->fetchAll();

Binding issues of slash with quotes

I am using php mysql-pdo and symfony
issue is : I have a query as stated below
WHERE STATUS IN (:STATUS)
with value of status as A','B.
and when i bind this as sting its escaping as below
WHERE STATUS IN ('A\',\'B')
and hence wrong output.
pleass help
Prepared statement can represent a complete data literal only. Not a part of literal, nor a complex expression, nor identifier. But either string or number only. Thus, your query doesn't work as you are actually binding a complex expression, not because of quotes
One have to create a query with placeholders representing every array member, and then bind this array values for execution:
$ids = array(1,2,3);
$stm = $pdo->prepare("SELECT * FROM t WHERE id IN (?,?,?)");
$stm->execute($ids);
To make this query more flexible, it's better to create a string with ?s dynamically:
$ids = array(1,2,3);
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($ids);
$data = $stm->fetchAll();
Of course, if we have other variables to be bound, we need to add them to values array:
$ids = array(1,2,3);
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in) AND category=?";
$stm = $db->prepare($sql);
$ids[] = $category; //adding another member to array
$stm->execute($ids);
$data = $stm->fetchAll();
the code become quite bloated but that's all PDO can offer to handle such complex cases. As a further improvement one can invent their own placeholders to support such complex data types.

Possible to have PHP MYSQL query ignore empty variable in WHERE clause?

Not sure how I can do this. Basically I have variables that are populated with a combobox and then passed on to form the filters for a MQSQL query via the where clause. What I need to do is allow the combo box to be left empty by the user and then have that variable ignored in the where clause. Is this possible?
i.e., from this code. Assume that the combobox that populates $value1 is left empty, is there any way to have this ignored and only the 2nd filter applied.
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND location = '$value1' AND english_name = $value2 ";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Thanks for any help.
C
Use
$where = "WHERE user_id = '$username'";
if(!empty($value1)){
$where .= "and location = '$value1'";
}
if(!empty($value2 )){
$where .= "and english_name= '$value2 '";
}
$query = "SELECT * FROM moth_sightings $where";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Several other answers mention the risk of SQL injection, and a couple explicitly mention using prepared statements, but none of them explicitly show how you might do that, which might be a big ask for a beginner.
My current preferred method of solving this problem uses a MySQL "IF" statement to check whether the parameter in question is null/empty/0 (depending on type). If it is empty, then it compares the field value against itself ( WHERE field1=field1 always returns true). If the parameter is not empty/null/zero, the field value is compared against the parameter.
So here's an example using MySQLi prepared statements (assuming $mysqli is an already-instantiated mysqli object):
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = ?
AND location = IF(? = '', location, ?)
AND english_name = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('ssss', $username, $value1, $value1, $value2);
$stmt->execute();
(I'm assuming that $value2 is a string based on the field name, despite the lack of quotes in OP's example SQL.)
There is no way in MySQLi to bind the same parameter to multiple placeholders within the statement, so we have to explicitly bind $value1 twice. The advantage that MySQLi has in this case is the explicit typing of the parameter - if we pass in $value1 as a string, we know that we need to compare it against the empty string ''. If $value1 were an integer value, we could explicitly declare that like so:
$stmt->bind_param('siis', $username, $value1, $value1, $value2);
and compare it against 0 instead.
Here is a PDO example using named parameters, because I think they result in much more readable code with less counting:
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = :user_id
AND location = IF(:location_id = '', location, :location_id)
AND english_name = :name";
$stmt = $pdo->prepare($sql);
$params = [
':user_id' => $username,
':location_id' => $value1,
':name' => $value2
];
$stmt->execute($params);
Note that with PDO named parameters, we can refer to :location_id multiple times in the query while only having to bind it once.
if ( isset($value1) )
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND location = '$value1' AND english_name = $value2 ";
else
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND english_name = $value2 ";
But, you can also make a function to return the query based on the inputs you have.
And also don't forget to escape your $values before generating the query.
1.) don't use the simply mysql php extension, use either the advanced mysqli extension or the much safer PDO / MDB2 wrappers.
2.) don't specify the full statement like that (apart from that you dont even encode and escape the values given...). Instead use something like this:
sprintf("SELECT * FROM moth_sightings WHERE 1=1 AND %s", ...);
Then fill that raw query using an array holding all values you actually get from your form:
$clause=array(
'user_id="'.$username.'"',
'location="'.$value1.'"',
'english_name="'.$value2.'"'
);
You can manipulate this array in any way, for example testing for empty values or whatever. Now just implode the array to complete the raw question from above:
sprintf("SELECT * FROM moth_sightings WHERE 1=1 AND %s",
implode(' AND ', $clause) );
Big advantage: even if the clause array is completely empty the query syntax is valid.
First, please read about SQL Injections.
Second, $r = mysql_numrows($result) should be $r = mysql_num_rows($result);
You can use IF in MySQL, something like this:
SELECT * FROM moth_sightings WHERE user_id = '$username' AND IF('$value1'!='',location = '$value1',1) AND IF('$value2'!='',english_name = '$value2',1); -- BUT PLEASE READ ABOUT SQL Injections. Your code is not safe.
Sure,
$sql = "";
if(!empty($value1))
$sql = "AND location = '{$value1}' ";
if(!empty($value2))
$sql .= "AND english_name = '{$value2}'";
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' {$sql} ";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Be aware of sql injection and deprecation of mysql_*, use mysqli or PDO instead
I thought of two other ways to solve this:
SELECT * FROM moth_sightings
WHERE
user_id = '$username'
AND location = '%$value1%'
AND english_name = $value2 ";
This will return results only for this user_id, where the location field contains $value1. If $value1 is empty, this will still return all rows for this user_id, blank or not.
OR
SELECT * FROM moth_sightings
WHERE
user_id = '$username'
AND (location = '$value1' OR location IS NULL OR location = '')
AND english_name = $value2 ";
This will give you all rows for this user_id that have $value1 for location or have blank values.

Categories