I have a problem with setting up a PDO query.
My PDO query looks like:
$query= "
SELECT COUNT(k.id) AS total
FROM members k
INNER JOIN members_subscription p ON p.id = k.id
WHERE k.status=?
AND k.gender IN (?,?)
AND country= ?
AND pic1 !=""
AND galer !=?
AND video !=?
AND birthday < ?
AND birthday > ?
AND purposes in(?,?,?,?) ";
The function that executes this query:
$rows_pr = sql_pdo_funct($query, array($status.$gender_one.$gender_two.$location.$gal_prm.$video_prm.$year_old.$purposes ));
If I set static parameters like:
$rows_pr = sql_pdo_funct($query, array(7,2,5,1,0,0,1999-08-08,1992-08-08,1,2,3,4));
I get correct value as a query result.
But if I'm trying to add dynamic values in PHP like:
$status = '7';
$gender = ',2,5';
$location= ',1';
$gal_prm= ',0';
$video_prm= ',0';
$year_old= ',1999-08-08,1992-08-08';
$purposes_prm= ',1,2,3,4';
And put that in sql_pdo_funct function I get error message:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number' in...
The function call:
$rows_pr = sql_pdo_funct($query,array($status.$gender.$location.$gal_prm.$video_prm.$year_old.$purposes ));
Why this error occurs?
What am I doing wrong and how this can be done?
Thank you for any help and advice.
If you pass the params in function sql_pdo_funct() as like -
$rows_pr = sql_pdo_funct($query, array($status.$gender_one.$gender_two.$location.$gal_prm.$video_prm.$year_old.$purposes ));
Then you can't get from function sql_pdo_funct() like as -
$rows_pr = sql_pdo_funct($query, array(7,2,5,1,0,0,1999-08-08,1992-08-08,1,2,3,4));
You will get like (As a String). Because you concat the String
$rows_pr = sql_pdo_funct($query, array("7,2,5,1,0,0,1999-08-08,1992-08-08,1,2,3,4"));
Note : You should quoted your parameters, If you want to pass the params as String separated.
Update your params like as below. Because you have 12 ? query string in your SQL Query, You should pass 12 params
$status = '7';
$gender = '2',
$gender2 = '5';
$location = '1';
$gal_prm = '0';
$video_prm= '0';
$year_old = '1999-08-08';
$year_old2= '1992-08-08';
$purposes_prm = '1';
$purposes_prm1 = '2';
$purposes_prm2 = '3';
$purposes_prm3 = '4';
$rows_pr = sql_pdo_funct($query,
array(
$status,
$gender_one,$gender_two,
$location,
$gal_prm,
$video_prm,
$year_old,
$year_old2 #Added another birthday params, Because there is 2 birthday conditions
$purposes_prm,$purposes_prm1,$purposes_prm2,$purposes_prm3 #Added more 3 params, Because there is total 4 params in `IN`
)
);
Syntax error,
array($status.$gender_one.$gender_two.$location.$gal_prm.$video_prm.$year_old.$purposes )
Try using the , instead of the .
The . is for concatenation, adding strings together, the comma , is for separating array elements.
Easy mistake to make.
Once you see that the error makes total sense, as your essentially concatining all your data into one array item, and therefor you query is looking for 4 items yet you only sent one.
Invalid parameter number
I prefer the named placeholders, makes it easier to keep track of stuff. It quite easy to do, just change these ? to the names like :country for country and then the same in the input array [':country' => 1 ....] etc. It's easer to read then [1,24,5,2 ... bla bla
UPDATE
This strikes me as wrong $purposes_prm= ',1,2,3,4'; this is one item not 4
Yea this wont work
If I set static parameters like:
$rows_pr = sql_pdo_funct($query, array(7,2,5,1,0,0,1999-08-08,1992-08-08,1,2,3,4));
I get correct value as a query result. But if I'm trying to add dynamic values in PHP like:
$status = '7';
$gender = ',2,5';
Those commas and stuff, yea not gonna work like that. The first part is good the part after But if I'm trying that's because it's not even a valid array when added like that.
Just replace the static values with corresponding variables.
When you use static values in array it have eleven elements , then just simply substitute these static values to variables(don not concatenate these variables)
Related
I have a form for users to enter some information. After the form being submitted, it should query a database with the values that the user entered.
My problem here is that if some of the values that the user entered are null, it should remove from the query.
This is my code:
if(isset($_POST['submit']))
{
include("../includes/header.php");
include ("../scripts/db/connect.php");
//Gets variables from $_POST
$negocio = $_POST['negocio'];
$imovel = $_POST['imovel'];
$distrito = $_POST['distrito'];
$concelho = $_POST['concelho'];
$freguesia = $_POST['freguesia'];
$query = "SELECT * FROM imoveis WHERE negocio = $negocio and imovel = $imovel and distrito = $distrito and concelho = $concelho and freguesia = $freguesia";
}
Imagine if $negocio, $imovel, $concelho and $freguesia are equal to null, the query should be:
$query = "SELECT * FROM imoveis WHERE distrito = $distrito;
How can I do this?
Generate your query string dynamcilly depending on which value are set
or not null, and than use that query
Run this code in a seperate file you will understand the point, after removing or adding comment to any variable, ($name,$street, $address or $qualification )
// you will see query will change depending on the set variable,
//I am using these name you can use any name for your variable
$name='my name';
//$address='some where on earth';
$street='this is my street';
//$qualification='i am very much qualified';
//now create the array only with the values which are not empty or not nul,
//I am using empty you can use null if you want with this example you can use any thing.
if(!empty($name)) $query_string_second_part[]=" AND name = '$name'";
if(!empty($address)) $query_string_second_part[]=" AND address = '$address'";
if(!empty($street)) $query_string_second_part[]=" AND street = '$street'";
if(!empty($qualification)) $query_string_second_part[]=" AND qualification = '$qualification'";
//hand type the first part for the query
$query_string_First_Part= "SELECT * FROM myTableName WHERE";
//Implode the array, if you want to see how it look like use echo,
$query_string_second_part= implode(" ", $query_string_second_part);
//as you can see we are adding AND with every value, so we need to remove the first AND
//with one space
//Make sure you give space in the second parameter. else it wont work means "" not correct but " " is correct
//Hint --> use one space in between the double qoutes
$query_string_second_part= preg_replace("/AND/", " ", $query_string_second_part, 1);
//Join the first and second part together to create a full query
$query_string=$query_string_First_Part.$query_string_second_part;
echo ($query_string);//see how our query look like at the moment
You can add an input null check to each clause. So for example where you do this:
distrito = $distrito
You might instead do this:
(distrito = $distrito or $distrito IS NULL)
or perhaps:
(distrito = $distrito or $distrito = '')
Depending on the data types, the actual input being used to build the query, etc. Might take some tweaking and debugging when manually building a query like this (I suspect using prepared statements with query parameters will make this cleaner, as well as more secure), but the idea is the same either way.
Basically you're instructing it to match the row based on the value, or match the row based on the lack of value. So for any given clause, if the supplied value is null/empty, then all rows match and the clause becomes moot.
Unless I am missing something very obvious, I would expect the values of $data1 and $data2 to be the same?? But for some reason when I run this scenario twice (its run once each function call so I'm calling the function twice) it produces different results.
Call 1: PDO = Blank, Sprintf = 3 rows returned
Call 2: PDO = 1 row, Sprintf = 4 rows (which includes the PDO row)
Can someone tell me what I'm missing or why on earth these might return different results?
$sql = "SELECT smacc.account as Smid,sappr.*,CONCAT('$domain/',filepath,new_filename) as Image
FROM `{$dp}table`.`territories` pt
JOIN `{$dp}table`.`approvals` sappr ON pt.approvalID = sappr.ID
JOIN `{$dp}table`.`sm_accounts` smacc ON pt.ID = smacc.posted_territory_id
LEFT JOIN `{$dp}table`.`uploaded_images` upimg ON pt.imageID = upimg.ID
WHERE postID = %s AND countryID = %s AND smacc.account IN (%s) AND languageID = %s";
echo sprintf($sql,$postID,$countryID,implode(',',$accs),$langID);
$qry1 = $db->prepare(str_replace('%s','?',$sql));
$qry1->execute(array($postID,$countryID,implode(',',$accs),$langID));
$data1 = $qry1->fetchAll();
print'<pre><h1>PDO</h1>';print_r($data1);print'</pre>';
$qry2 = $db->query(sprintf($sql,$postID,$countryID,implode(',',$accs),$langID));
$data2 = $qry2->fetchAll();
print'<pre><h1>Sprintf</h1>';print_r($data2);print'</pre><hr />';
The root of the problem is the implode(',',$accs) function.
While you are using sprintf() it will generate a coma separated list and that list will be injected into the query string.
The result will be something like this:
smacc.account IN (1,2,3,4,5)
When you are binding the same list with PDO, it handles it as one value (a string: '1,2,3,4,5'). The "result" will be something like this:
smacc.account IN ('1,2,3,4,5')
Note the apostrophes! -> The queries are not identical.
In short, when you are using PDO and binding parameters, you have to bind each value individually (you can not pass lists as a string).
You can generate the query based on the input array like this:
$query = ... 'IN (?' . str_repeat(', ?', count($accs)-1) . ')' ...
// or
$query = ... 'IN (' . substr(str_repeat('?,', count($accs)), 0, -1) . ')'
This will add a bindable parameter position for each input value in the array. Now you can bind the parameters individually.
$params = array_merge(array($postID, $countryID), $accs, array($langID));
$qry1->execute($params);
Yes as Kris has mentioned the issue with this is the IN part of the query. Example 5 on the following link helps fix this: http://php.net/manual/en/pdostatement.execute.php. I tried using bindParam() but that didn't seem to work so will use Example 5 instead.
I haven't worked with this method of prepared statements until recently and i am having a problem. I read some of the similar issues here on stackoverflow but they seem to refer to different things like duplicate markers (i'm using ? not :name so i presume that doesn't apply) and i have used xdebug in php storm to debug what is being passed.
The query is part of a filter i use in my script.
My querystring in the debugger shows this:
SELECT * FROM data_table WHERE (hud_game_type = ? OR hud_game_type = ? OR hud_game_type = ?) AND (hud_table_type = ? OR hud_table_type = ? OR hud_table_type = ?) AND (hud_table_size = ? OR hud_table_size = ? OR hud_table_size = ? OR hud_table_size = ?) AND approve = ? ORDER BY hud_downloads DESC
So there is clearly 11 x ?
$search_values in debugger shows:
0 = "Omaha"
1 = "Holdem"
2 = "All"
3 = "Cash"
4 = "Tourney"
5 = "All"
6 = "6max"
7 = "FR"
8 = "HU"
9 = "All"
10 = "1"
So again clearly 11 values in the array, here is the final part of the code:
Here is the code but as mentioned above the built string is above and it seems to be legit, since this code is relying on values passed in a form its built in another function, i can add that if relevant but i wouldn't think so since i show the outputted query above
$result = $database->resultset("
SELECT * FROM $data_table WHERE $search_game_type AND $search_table_type AND $search_table_size AND approve = ? $search_sort_by",(array($search_values)));
// returns an array of the results, first we execute and then fetch the results
public function resultset($query,$values){
$this->stmt = $this->dbh->prepare($query);
try{
// this handles situation where no params need to be escaped.
if($values == ""){
$this->stmt->execute();
} else{
$this->stmt->execute($values);
}
} catch (PDOException $e){
$this->error_db_query_failed(true,$values,$e->getMessage(),"Error #12");
}
Now the error returned from PDO is:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
This same resultset function seems to work fine in other parts of the code but i'm only passing one or two params in those.
Even if someone can point me in the right direction or suggest what the error might be i am happy to search further but a lot of the other errors refer to :name type binding so the process is different.
Since $search_values is already an array, you should just pass it directly to the function, don't use array($search_values).
There is MySQL query with unknown number of parameters (dynamically built) of the following format:
SELECT * FROM actions WHERE user1 = ? AND user10 = ? AND user11 = ? AND time = :time
Since I am using PDO prepared statement, I can't mix named and positional parameters in one query. So I need to repalce all question marks with named parameters sequentialy. I have an Array() that holds these parameters:
$parameters = Array();
$parameters['time'] = 1234567;
Now I need to replace every question mark with a sequential named parameter, so the query will look like this:
SELECT * FROM actions WHERE user1 = :user0 AND user10 = :user1 AND user11 = :user2 AND time = :time
And $parameters would contain every named parameter in it. I need to find every " ? " in the query string and looping through the occurences, replace them with an incrementing string. In JavaScript I would use a regex to find ? and pass them to a function, which would have a global incrementing variable to track the current named parameter number, but I have no idea how to do this in PHP.
P.S. Besides replacing them sequentially, I would also need to add parameters to array:
$parameters['user0'] = $user;
$parameters['user1'] = $user;
$parameters['user2'] = $user;
You are doing something extremely strange.
First of all, there is not a single reason to combine different placeholders. Just make it
AND time = ?
and then just execute.
Yet if you want named parameters - add them at the same time you are building a query.
Apparently, user1 = ? AND user10 = ? AND user11 = ? being dynamically built. Why don't you just add named placeholders at the same time?
BTW, if you have to bind the same variable to all the marks, then just use the the single named placeholder for all
WHERE user1 = :user AND user10 = :user AND user11 = :user AND time = :time
then turn emulation mode off and then execute with
$stmt->execute(array('user'=>$user,'time'=>123));
All I was looking for:
$count = 0;
preg_replace_callback(
"/\\?/",
function ($matches) {
global $count;
return ':user' . (++$count);
},
$query_string
);
Which would return exactly what is needed.
I am completely stumped. Here is my php (CodeIgniter) code:
function mod()
{
$uid = $this->session->userdata('uid');
$pid = $this->input->post('pid');
if ($this->_verify($uid,$pid))
{
$name = $this->input->post('name');
$price = $this->input->post('price');
$curr = $this->input->post('curr');
$url = $this->input->post('url');
$query = $this->db->query("UPDATE items SET
name=".$this->db->escape($name).",
price=".$this->db->escape($price).",
currency=".$this->db->escape($curr),",
url=".$this->db->escape($url)."
WHERE pid=".$this->db->escape($pid)." LIMIT 1");
}
header('location: '.$this->session->userdata('current'));
}
The purpose of this code is to modify the properties (name, price, currency, url) of a row in the 'items' table (priary key is pid). However, for some reason, allowing this function to run once modifies the name, price, currency and url of ALL entries in the table, regardless of their pid and of the LIMIT 1 thing I tacked on the end of the query. It's as if the last line of the query is being completely ignored.
As if this wasn't strange enough, I replaced "$query = $this->db->query(" with an "echo" to see the SQL query being run, and it outputs a query much like I would expect:
UPDATE items
SET name = 'newname',
price = 'newprice',
currency = 'newcurrency',
url = 'newurl'
WHERE pid = '10'
LIMIT 1
Copy-pasting this into a MySQL window acts exactly as I want: it modifies the row with the selected pid.
What is going on here???
Now I feel stupid: all it took was seeing my code in a different font. My code has
currency=".$this->db->escape($curr),",
instead of
currency=".$this->db->escape($curr).",
The echoing made it work just fine because apparently you can give echo more than one string, comma separated, and it concatenates them
cries I spent hours on this
I know you answered your own question, but let me just add this to the pile: You're not leveraging CodeIgniter AT ALL in this sort of query - which if you used CI as it's intended, you wouldn't have had that typo. Your query should look like this (among other things):
$query = $this->db->update('items',
array('name' => $this->input->post('name'),
'price' => $this->input->post('price'),
'curr' => $this->input->post('curr')),
array('id' => $this->input->post('id')),
1);
By assembling the query string by hand, you're undoing what CI does for you. Only when you're using some complex JOIN statement should you be writing your own SQL in CI, and even then, you want to use the sprintf PHP function to make sure you're not introducing typos.