PDO: Cost of Calling prepare() in a loop? - php

I'm writing a database class for my website with functions such as fetchOne, fetchAll which prepare, execute (+ bind), and fetch the query all in one so I don't have to individually call those functions every time. Some cron jobs on my site execute thousands, or even millions of queries inside of a loop.
Would using my class cause the statement to be re-prepared each iteration of the loop or would PDO "remember" the query has already been prepared? Would this significantly impact performance and if so could the solution be to just provide a function that passes the database instance and do something like $db->getDb()->prepare($query); outside of the loop? Or is there a better solution?
Example function:
public function fetchOne($query, $params = array(), $fetchMode = PDO::FETCH_ASSOC)
{
$stmt = self::prepareExecute($query, $params);
$result = $stmt->fetch($fetchMode);
if (count($result) < 1)
$result = FALSE;
$stmt->closeCursor();
unset($stmt);
return($result);
}

you would not want to "re-prepare" the same query multiple times. this is the purpose of the prepare statement. you prepare it once, bind the variable(s), then you simply switch the variables' values and re-execute each iteration of the loop.
http://www.php.net/manual/en/pdostatement.bindparam.php
doing it this way will greatly increase your performance over alternative methods.

Related

Do i need a "return" statement when using fetchAll() in a function?

I'm testing in PHP a simple function to take data in a faster way, simply putting a query argument and creating an array using fetchAll().
Since fetchAll() is returning an array, do I need the "return" statement or not?
PHP
function data_extraction($query) {
$query = $co->prepare($sql);
$query->execute()->fetchAll(PDO::FETCH_ASSOC);
// do I need "return $query"?;
}
Based on your comment that:
i'm using this function to extract some results that i need to use later in the program
It does sound like you need to return the results of fetchAll() from the function.
This is easily achieved:
function data_extraction($sql) {
$query = $co->prepare($sql);
$query->execute();
$arr = $query->fetchAll(PDO::FETCH_ASSOC);
return $arr;
}
This will return the array produced by fetchAll() to callers of your function.
Note: You probably don't want to return $query as this will give an outside caller access to your prepared statement.
To utilise in your code:
...
$results = data_extraction('SELECT xyz...');
Replacing the SELECT xyz... statement with your chosen SQL statement.
NOTE
Following an important comment by #YourCommonSense regarding parameterisation of PDO prepared statements, I urge extreme caution when injecting user content into SQL statements without the use of parameters. The reference provided by #YourCommonSense is here which has a glorious description of the issue and how to resolve it.
This answer is to primarily address the question Do I use a return statement? not How do I safely use PDO prepared statements

PDO and executing code

Just a quick question around executing PDO statements.
Below is my query
$setDate = "INSERT INTO wl_datecheck (inputdate) VALUES (UNIX_TIMESTAMP(STR_TO_DATE($reportDate, '%Y%m%d')))";
Now, what I am doing is setting the query & connection as the variable $data as follows:
$data = $conn->query($setDate);
What I need to know, is does that ^^ alone execute or do I need to include
$data->execute();
The reason for asking is that I seem to be getting duplicate content from the INSERT statement & I'm not sure why
You are indeed doing it twice, you either use Query or Execute.
From the Docs:
PDO::query() executes an SQL statement in a single function call,
returning the result set (if any) returned by the statement as a
PDOStatement object.
For a query that you need to issue multiple times, you will realize
better performance if you prepare a PDOStatement object using
PDO::prepare() and issue the statement with multiple calls to
PDOStatement::execute().
http://us2.php.net/pdo.query
You can also simply check the result of the Insert statement by using rowCount()
$data = $conn->query($setDate);
if($data->rowCount() >= 1){
//echo "Inserted";
}else{
//echo "An error occurred while inserting";
//$arr = $data->errorInfo();
//print_r($arr);
}
And no need to call execute()

Getting the results of a variable length prepared statement in MySQLi

I writing an accounting website which has quite a few MySQL statements in it. To prevent SQL injection I use prepared statements for any data which is put in by the user.
In order to prevent having to write the steps of preparing and binding statements I have the following function:
function executeSql($mysqli,$query_string,$params=null,$paramtypes=null){
$nr_params=strlen($paramtypes);
$query_type = substr($query_string,0,3);
$stmt = $mysqli->prepare($query_string);
$queryParams[] = $paramtypes;
$counter=1;
if($nr_params>1){
while($counter<=$nr_params){
$queryParams[$counter]=&$params[$counter-1];
$counter++;
}
} else {
$queryParams[1]=&$params;
}
// Actual binding of the statement. Taking into account a variable numbers of '?' in the query string.
call_user_func_array(array($stmt,'bind_param'),$queryParams);
// Execution of the statement
$stmt->execute();
// Part where i'd like to have a substitute for:
$result = $stmt->get_result();
return $result;
}
In the last part I'd like to return the result because then using the result I can treat each row. The problem is that the mysqlnd driver is not installed on the production server so the function $stmt->get_result() cannot be used. I tried to bind the result into variables but then again, every query returns a different number of columns.
Anyone has an idea how to tackle this?
So in summary (in response to the comments):
How can I retrieve a results object of an executed MySQLi statement while I cannot use $stmt->get_result();
Kind regards,
EJG
PS I know the code is not flawless, e.g. if strings are used as variables to bind to the statement but that is easily fixed.
UPDATE:
I came across the function $stmt->result_metadata(); Although supposedly the function name suggests only the meta data the php documentation states that:
"If a statement passed to mysqli_prepare() is one that produces a result set, mysqli_stmt_result_metadata() returns the result object"...

pdo prepared statement multiple execution?

I'm just getting to implement PDO in my site but I was wondering if it is possible to execute prepared statement multiple times?
$SQL = $dbh->prepare("SELECT * FROM user WHERE id=? AND users=?");
$SQL -> execute(array($id,$userid));
while($check = $SQL -> fetchObject()){
and then I would use a while loop for this SQL
Can I use the same $SQL for another execution within the while loop? So I don't have to type in the prepared statement again?
$SQL -> execute(array($id2,$userid2));
while($check2 = $SQL ->fetchObject(){
//while loops for second execution, but I'm not sure how it works cause
//its using the same $SQL?
}
}//this end bracket is for the first while loop
Yes, you can reuse the same prepared statement, but not how you have it in the question. What you are trying to do is essentially the same as doing this:
for ($i=0; $i<$some_number; $i++) {
echo $i."\n";
for ($i=0; $i<$some_number; $i++) {
// do something
}
}
The the second for loop moves the same pointer as the original one, so therefore the output from the above would simply be "0" indicating that the original for loop only happened once.
So in order to get around this, you will need to store the results of the first execute into an array and then iterate over it. That way you won't have to worry about any pointers
$SQL = $dbh->prepare("SELECT * FROM user WHERE id=? AND users=?");
$SQL->execute(array($id,$userid));
$checks = $SQL->fetchAll();
foreach ($checks as $check) {
$SQL->execute(array($id2,$userid2));
while ($check2 = $SQL->fetchObject(){
//while loops for second execution
}
}
This way, you are using exactly the same prepared statement (which is good) and the original $check variable is available to be used in the while loop.
However, with all that said, I have a strong hunch that you can probably get everything into one single SQL query without the need for looping over it like this.
Yes, it is possible. Prepare once, execute as many times as you need to.
Of course, I'm not sure why you're doing a SELECT in a loop... that typically doesn't make much sense.

bind_param and execute in loop?

Is it possible to invoke bind_param and execute iteratively, or must I prepare a statement at the beginning of each iteration?
$query = $db->prepare('...');
foreach ($dataItem as $item) {
$query->bind_param($v1, $v2, ..., $item);
$query->execute();
}
$query->close();
If I do have to recreate the statement each iteration, is it possible to optimize this process?
Thank you!
There is no need to prepare a statement at the beginning of each iteration.
The concept of prepared statements is to reuse the same statement multiple times in the first place, so it's good to go to prepare once and execute it multiple times.
See also this note on the manual page.

Categories