I have a (semi) simple MySQL query that I'm trying to use (via php mysqli extension), and I can't quite figure out how to do this.
My query looks like
SELECT DISTINCT Col1 from `table1` where `col2`= ? and `col3`=?
UNION SELECT DISTINCT Col1 from `table2` where `col2`=(?) and `col3`=(?)
I have two tables that I don't want to deal with merging and I just want to reuse the original two prepared "?"s. I know there is something I can do for this when inserting values into a table, but my efforts in searching the docs have thus far proved useless.
Can I do this, and how?
update
Here's my code
$query='SELECT DISTINCT enginesizecc FROM `table1` where year=? and vehicle_make= ? as UNION SELECT DISTINCT enginesizecc from `table2` WHERE year=(?) AND vehicle_make =(?)';
$stmt=$sql->prepare($query);
echo $sql->error; //I'm in debug mode
$blank='';
if(array_key_exists('year', $_POST)){
if(array_key_exists('make', $_POST)){
$stmt->bind_param('ss', $_POST['year'], $_POST['make']);
}
else $stmt->bind_param('ss', $_POST['year'], $blank);
}
elseif(array_key_exists('make', $_POST)){
$stmt->bind_param('ss', $blank, $_POST['make']);
}
else{
//if(array_key_exists('model', $_POST)) $stmt->bind_param('sss', $blank, $blank);
$stmt->bind_param('ss', $blank, $blank);
}
$stmt->execute();
$modelItem='';
$stmt->bind_result($modelItem);
$models=array();
while($stmt->fetch()){
$models[]=$modelItem;
}
sort($models);
return $models;
I know that I could just bind the same variables twice, but that seems rather inefficient.
PDO allows you to name parameters specifically, like so, but MySQLi doesn't support named variables:
"SELECT x FROM y WHERE name = :name and key = :key"
In PDO this would let you re-use :name and :key after specifying their types. I'm not arguing over which is better, since you can achieve the same thing in MySQLi.
The thing is that MySQLi makes it rather hard to stick to the "Don't Repeat Yourself (DRY)" methodology. (Consider custom functions if you like DRY).
It's the reason some prefer PDO over MySQLi, but there are some funky workarounds (such as call_user_func_array in custom functions, etc).
And as to your "efficiency" comment, it really makes no difference to repeat the variables. It will be parameterized in the MySQL API call twice, but it hardly affects performance significantly. PDO parameterizes internally without using MySQL (unless you explicitly make it use MySQL), and MySQLi makes the MySQL API parameterize for it.
Here is an example on how to bind parameters to a query.
global $dbconnection;
$sql = "INSERT INTO Sales(order_id,client_id,sale_date,status) VALUES (?,?,?,?)";
if ($stmt = $dbconnection->prepare($sql))
{
/* Bind our params */
$stmt->bind_param('iisi',$order_id,$client_id,$today,$status);
$stmt->execute();
$stmt->close();
}
else
{
print "ERROR";
}
Related
This is a request for confirmation/clarification based on a very easily missed comment from 6 years ago in the PHP.net manual for PDO::prepare, which I haven't seen discussed elsewhere (even in the great phpdelusions blog). It's such a powerful possibility if it's true that I feel it deserves a little wider coverage and search highlighting for others (or needs debunking if not).
Here's the comment (by Hayley Watson):
It is possible to prepare in advance several statements against a single connection.
As long as that connection remains open the statements can be executed and fetched from
as often as you like in any order; their "prepare-execute-fetch" steps can be interleaved
in whichever way is best.
So if you're likely to be using several statements often (perhaps within a loop of
transactions), you may like to consider preparing all the statements you'll be using up front.
I have some code that must run like (pseudocode):
foreach (fetchAll row with PDO) {
process row results
if (condition)
update table with processed results
else
delete row no longer needed
}
According to that comment, I could create TWO prepared statements BEFORE the loop, one for the update query and one for the delete query, then execute (only) within the loop. As long as the handles are different and preserved, the connection should cache both, I can use them interchangeably and I'd not have to do any SQL statement parsing INSIDE the loop, which would be very inefficient:
// do statement prepare/execute/fetchAll for main loop, then...
$update_stmt = $PDO->prepare($update_query);
$delete_stmt = $PDO->prepare($delete_query);
foreach (fetchAll row) {
process row results
if (condition)
$update_stmt->execute(some_processed_values);
else
$delete_stmt->execute(some_other_values);
}
Since most questions here only discuss using one prepared statement at a time, and this has excellent implications for code efficiency if widely applied, would anyone like to confirm that this is definitely the case (at least from PHP7)? If so, I guess other neat applications for this form of code could be shared in the solutions.
There's no problem with using multiple prepared statements at the same time and executing them out of order.
You can run code such as the following with intertwined statements and it will work.
$stmt1 = $pdo->prepare('INSERT INTO addplate(Plate) VALUES(?)');
$stmt2 = $pdo->prepare('UPDATE addplate SET Plate=? WHERE Plate=?');
$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
This can bring you some performance benefit when for some reason you can't avoid N+1 problem. You prepare the inner query once and then execute it inside the loop multiple times.
However, this could be a problem with result-producing queries if you want to run unbuffered query (it's very rarely used). PDO by default executes buffered queries, so you need to switch them off to run into this issue.
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt1 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt2 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt1->execute(['val1']);
var_dump($stmt1->fetch());
$stmt2->execute(['val2']); // <-- Error if stmt1 still has more records
var_dump($stmt2->fetch());
It will produce:
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
Nice Question! First statement ?
$id = '2';
$username = 'John';
if ($id === 2){
$array = array(':username'=> $username, ':id'=>$id);
$sql = "UPDATE users SET username = ? WHERE id = ?";
}else{
$array = array(':id'=>$id);
$sql = "DELETE FROM users WHERE id = ?";
}
$stmt = $pdo->prepare($sql);
$stmt->execute($array);
Second statement :
$sql = "UPDATE users SET username = ? WHERE id = ?";
$sql = "DELETE FROM users WHERE id = ?";
$stmt1 = $pdo->prepare($sql);
$stmt2 = $pdo->prepare($sql);
if ($id === 2){
$array = array(':username'=> $username, ':id'=>$id);
$stmt1->execute($array);
}else{
$array = array(':id'=>$id);
$stmt2->execute($array);
}
Simple statements:
$stmt = $pdo->prepare("UPDATE users SET username = ? WHERE id = ?")->execute([':username'=> $username, ':id'=>$id]);
$stmt = null;
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([':id'=>$id]);
$stmt = null;
First statement run 1 second faster than second statement.
Runing separete statements at the bottom, both update and delete at same time much faster than others.
is that because of if statements ?
I am runing php 7.4.0 and mysql 8.0
Updated if someone wants to try.
this is my select command
$stmt = $this->conn->prepare("SELECT id,task FROM tbl_all_task WHERE status = 0");
(there are multiple rows having status ).
i tried $stmt->fetchall() , $stmt->fetchall() etc. Nothing works.
I need all rows so that i could make a JSON ARRAY and return this to mu function call.
after you use prepare(), you get a chance to make a 'prepared statement',
and bind values to your query (see bindValue()):
Many of the more mature databases support the concept of prepared statements. What are they? They can be thought of as a kind of compiled template for the SQL that an application wants to run, that can be customized using variable parameters.
after you prepare and (optionally) bind, you must execute();
after which, if every thing went right, you can use fetching methods such as fetchAll().
try it like this:
$stmt = $this->conn->prepare("SELECT id,task FROM tbl_all_task WHERE status = 0");
$stmt->execute();
if ($data = $stmt->fetchAll()) {
print_r(json_encode($data));
}
if you're not interested in a prepared statement (altough it is generally the prefered way), you can just use the query() method directly:
$stmt = $this->conn->query("SELECT id,task FROM tbl_all_task WHERE status = 0");
if ($data = $stmt->fetchAll()) {
print_r(json_encode($data));
}
I'm trying to execute this:
$colparam = 'abcd';
$stmt = $db->prepare("SELECT DISTINCT ? AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute(array($colparam));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
and it's not working (no errors, just empty array as result).
Instead this works fine:
$stmt = $db->prepare("SELECT DISTINCT abcd AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
So is there any catch with the use of parameter as a name of the column in PDO?
No, you can't use parameter replacements for any database objects (tables, columns, etc.) in MySQL.
When you think about what a prepared statement actually is, this makes complete sense. As how can MySQL prepare a query execution plan when it does not even know the database objects involved.
I certainly wish that more documentation would actually cover what a prepared statement actually does (beyond it's obvious use for parametrization).
Here is link to MySQL prepared statement documentation for more reading:
https://dev.mysql.com/doc/refman/5.6/en/sql-syntax-prepared-statements.html
I'm trying to create a function that allows me to delete a certain row in a table. I'd like to use the function for a few different tables so when I call the function I would make one of the parameters of the functions the table name. Here's an example of what I'm trying to do:
function delete($conn, $table, $id) {
$stmt = $conn->prepare("DELETE FROM ".$table." WHERE id = :id");
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->rowCount() > 0) {
return true;
} else {
return false;
}
}
One problem I'm having though is that because the $table variable goes straight into the SQL Query, wouldn't my database be under risk of SQL Injection?
As I learnt from one of my other questions, I can't just put :table and add it to the bindParam function, so I don't know how to make this function safe. Any ideas??
Sanitize the table data
You can define an array of whitelisted table names to use in your function:
$whitelist = array('table1', 'table2', ...)
and then use:
$myTable= array_intersect_keys($table, array_flip($whitelist));
$myTable will now be safe.
Table names as metadata are much more restricted than row data - so sanitizing a table name should be much more reliable than sanitizing data. By this I ofcourse mean sanitizing outside of PDO.
What we use quite often is a PHP include, that contains all valid table names as an array definition, so we just look it up - if it is not found, and the file age of the include is more than an hour, we run "SHOW TABLES" and recreate the include, so it's quite automagic.
MySQL doesn't currently support dynamic SQL string building, but you could try a whitelist of tables or possibly a MySQL prepared statement with PDO's prepared statements (redundant, I know).
$sql = "
PREPARE stmt FROM
'DELETE FROM #tbl WHERE id = :id';
EXECUTE stmt USING :table;
";
$stmt = $db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->bindParam(':table', $table);
This is untested, but may help.
I want to create prepared stmt such as
"SELECT id,latitude,longitude
FROM sometable WHERE
(3437.74677 * ACOS(SIN(latitude_radians) * SIN(?) +
COS(latitude_radians)*COS(?) * COS(longitude_radians - ?))) <= ?";
in PHP. Where clause is a function of column values and bind variables
but how should I bind the values. Or is this even a legal prepared stmt?
Thanks in advance,
-v-
I don't see any problem here.
See:
PHP Prepared Statements.
Extremely minimal sample:
$stmt = $dbh->prepare( $QUERY );
$stmt->execute( $arguments_array )
Sorry for being unclear.
As I understand following is an example of PHP prepared stmt,
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name'])))
Now the where clause is always a simple, column = ? AND column2 = ?. In my case though its function and it didnt work when assigned values to the bind variables. I will regenerate the error again and post it.
I was wondering then if it is even legal to have a function of column names and bind variables in the where clause.