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.
Related
I have a PHP array with different items and their respective values, and would need to update each row for each item in a database. Is there a way to only have one query to execute, instead of multiple foreach queries?
foreach($array as $key=>$item){
$image_id = $item['id'];
$order_id = $item['order_id'];
$sql = "UPDATE images SET order_id='$order_id' WHERE id='$image_id'";
$query = mysqli_query($connection, $sql);
} // end for each
You can't really combine it into one query using UPDATE, as the value and condition come in pairs (the value and condition is different for each row you are updating).
There is a hacky fix with INSERT INTO..ON DUPLICATE KEY UPDATE, but I would refrain from using that - see the question linked by RiggsFolly in the comments.
When using a prepared statement, you can execute the same query multiple times (you would have to query it multiple times anyways, even with mysqli_query(), but this is optimized and more secure when using a prepared statement).
$sql = "UPDATE images SET order_id=? WHERE id=?";
$stmt = $connection->prepare($sql);
foreach($array as $key=>$item){
$stmt->bind_param("ss", $item['order_id'], $item['id']);
$stmt->execute();
}
$stmt->close();
If your IDs are integers instead of strings, replace the respective s with an i.
PHP.net on mysqli::prepare()
PHP.net on mysqli-stmt::bind_param()
PHP.net on mysqli-stmt::execute()
There are many questions on SO about this but I cannot find one that quite meets my situation.
I want to use the values in some fields/columns of a table to set the value of a third field/column
In other words something like:
table races
athleteid|difficulty|score|adjustedscore
$sqlSelect = "SELECT athleteid,difficulty,score FROM races";
$res = mysql_query($sqlSelect) or die(mysql_error());
while ($row = mysql_fetch_array($res)){
$adjustedscore=difficulty*score;
$sqlupdate = "UPDATE race, set adjustedscore = '$adjustedscore' WHERE athletes = 'athletes'";
$resupdate = mysql_query($sqlupdate);
}
My understanding, however, is that MYSQL does not support update queries nested in select ones.
Note, I have simplified this slightly. I am actually calculating the score based on a lot of other variables as well--and may join some tables to get other inputs--but this is the basic principal.
Thanks for any suggestions
You can run:
UPDATE `races`
SET `adjustedscore` = `difficulty` * `score`
WHERE `athleteid` IN (1, 2, 3, ...)
First of all, as previous commentators said, you should use PDO instead of mysql_* queries.
Read about PDO here.
When you'll get data from DB with your SELECT query, you'll get array. I recommend you to use fetchAll() from PDO documentation.
So, your goal is to save this data in some variable. Like you did with $row.
After that you'll need to loop over each array and get your data:
foreach($row as $r) {
//We do this to access each of ours athlete data
$adjustedscore= $row[$r]["difficulty"]* $row[$r]["score"];
//Next row is not clear for me...
$query = "UPDATE race SET adjustedscore = '$adjustedscore' WHERE athletes = 'athletes'";
And to update we use PDO update prepared statement
$stmt = $dbh->prepare($query);
$stmt->execute();
}
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 in the process of migrating from mySQL based php to PDO. I'm working an if statement. Basically, I want to query a value from the database and check if anything gets returned.
With mySQL I would have done this:
//Query the entered data to see if it matches
$myQuery = "SELECT * FROM $tbl_name WHERE myData ='$MyData'";
$myResult = mysql_query($myQuery);
// If statement
if( mysql_num_rows($myResult) >=1 ) {
$result .= "The data matched and SQL query was successful";
}
With PDO I have this so far, although it's not working yet:
//Query the entered data to see if it matches
$myQuery = "SELECT myData FROM table_name WHERE myData = '$myData'";
$myResult = $db->single($myQuery);
// If statement
if($myResult == 1) {
$result .= "The data matched and PDO query was successful";
}
Although, from what I've seen you don't need to separate the query and the result with PDO and could combine both the query and result into a single statement.
$myQuery = $db->prepare("SELECT myData FROM table_name WHERE myData = ?");
$myQuery->execute(array($myData));
$myResult->rowCount($myQuery);
// If statement
if($myResult == 1) {
$result .= "The data matched and SQL query was successful";
}
In PDO you would do it like this:
$data_exists = $dbh->prepare('SELECT 1
FROM `table_name`
WHERE `myData` = ?');
$data_exists->execute(array($myData));
if ($data_exists->rowCount() == 1) {
$result .= 'The data matched and PDO query was successful';
}
To answer your question you could use PDO's function rowCount().
if ($myResult->rowCount() > 0) {
$result .= 'The data matched and PDO query was successful';
}
But notice that your PDO query is not safe. It is still vulnerable for MySQL injection since you directly insert a variable into your query. Use PDO's function bindParam() or bindValue() instead.
Although, from what I've seen you don't need to separate the query and the result with PDO and could combine both the query and result into a single statement.
It does allow it the way you shouldn't use PDO anyway. but nevertheless, your single function is not PDO and I doubt it will allow you any num rows. But you don't need that number anyway.
So, to make things straight
Your single() function ought to have support for prepared statements.
Having result from this function, you need no num rows at all
Here it goes
public function single($myQuery, $params = NULL, $style = PDO::FETCH_ASSOC)
{
$stmt = $this->pdo->prepare($myQuery);
$stmt->execute($params);
return $stmt->fetch($style);
}
$myQuery = "SELECT myData FROM table_name WHERE myData = ?";
$myResult = $db->single($myQuery, [$myData]);
if($myResult) {
$result .= "The data matched and PDO query was successful";
}
if you don't need the result itself, then you can shorten a bit
$myQuery = "SELECT 1 FROM table_name WHERE myData = ?";
if($db->single($myQuery, [$myData])) {
$result .= "The data matched and PDO query was successful";
}
Ok. Thank you everyone for your answers. After taking them into consideration and doing some testing, I'd like share some findings with you.
In regards to issue at hand, which is setting up an if statement when checking database values with PDO I've noticed the a few differences. This may be obvious to some of you, but for someone coming from mysql (like me) it's not.
After doing some testing I found that I couldn't query a string with PDO as you would with mysql. Furthermore, in mysql you are able to set the string value prior to querying it in the database.
From what I can see, PDO will only query and return arrays. So you can't simply insert a string value and check for it.
In regards my question, I found that using that:
My querying PDO syntax was incorrect.
When it was correct, I was only able to query an array which returns an array.
Anyway this is what i did:
// Get's the entire column called "ColumnName"
$getColumn = $db->column("SELECT columName FROM table_name ");
// Check if the returned array contains a string.
$myString = "matchThisString";
if (in_array($myString, $getColumn)) {
echo "A Match was found";
}
else {
echo "No match was found";
}
I would recommend reading the following:
http://www.php.net/manual/en/pdostatement.fetch.php
http://www.php.net/manual/en/language.types.boolean.php
http://www.php.net/manual/en/language.types.array.php
All answers suggesting to use rowCount() are wrong, rowCount() returns a non zero value only in operations that Affect rows, not selected rows, or at least as sated in PHP manual: PDO::Statement it's not guarantied.
PDOStatement::rowCount() returns the number of rows
affected by
the last DELETE, INSERT, or UPDATE statement executed by the corresponding
PDOStatement object.
If the last SQL statement executed by the associated PDOStatement was a SELECT
statement, some databases may return the number of rows returned by that statement.
However, this behaviour is not guaranteed for all databases and should not be
relied on for portable applications.
More Text From The Manual
Example #2 Counting rows returned by a SELECT statement
For most databases, PDOStatement::rowCount() does not return the
number of rows affected by a SELECT statement. Instead, use
PDO::query() to issue a SELECT COUNT(*) statement with the same
predicates as your intended SELECT statement, then use
PDOStatement::fetchColumn() to retrieve the number of rows that
will be returned. Your application can then perform the correct
action.
At least I know that I had tons of problems with it.
One other way is to use:
$rows = stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($rows))
{
//Do something here
}
else
{
//Do something else here.
}
The above chunk of code is good if you expect small data sets returned. If we're talking about huge data sets, then you're better of executing 2 queries, one with a COUNT in it with the same conditions as your select statement and then the SELECT statement like this:
$sql = "SELECT COUNT(*) FROM $tbl_name WHERE myData ='$MyData";
$count = $db->query($sql)->fetch();
if ($count[0] > 0)
{
$myQuery = "SELECT * FROM $tbl_name WHERE myData ='$MyData'";
//.... Continue your code here since now you know for sure that your
//query will return rows....
//.....
}
else
{
//handle the case that no data were returned.
}
The code chunks above are just for demonstration, so you can understand what and how to do.
I can't guarantee that this is the best way to do it, it's just the way I do it. Cheers.
Also a better way to do your thing is to use Prepared Statements as someone or some suggested. That has nothing to do with the way you're gonna deal with your problem here, it's just a safer practice in general since prepared statements prevent SQL Injection if not completely then to a really good degree (not sure about it).
You can do something like this :
$sql = "SELECT * FROM animals";
$stmt = $dbh->query($sql);
$result_count = $stmt->fetch(PDO::FETCH_ASSOC);
if($result_count == 1)
{
$result .= "The data matched and PDO query was successful";
}
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.