Much have been written about the benefits of using PDO::prepare, but little has been written on the benefits of using PDO::query. I believe PDO::query is created to serve a purpose and there ought to be some relative advantage of using this function over PDO::prepare.
I have a query that goes like this:
SELECT * from Table ORDER BY id DESC LIMIT 100;
This query contains no user input for escaping and no variables for repeated querying. Should I use PDO::query, go back to mysqli_query or stick to PDO::prepare in this case?
UPDATE: Further examination on the general query log shows this for both PDO::prepare and PDO::query:
22 Connect user#localhost on Database
22 Prepare SELECT * from Table ORDER BY id DESC LIMIT 100
22 Execute SELECT * from Table ORDER BY id DESC LIMIT 100
22 Close stmt
22 Quit
I was expecting PDO::query to produce:
22 Connect user#localhost on Database
22 Query SELECT * from Table ORDER BY id DESC LIMIT 100
22 Quit
But this only happens, and to both, when setAttribute(PDO::ATTR_EMULATE_PREPARES, true). I am quite surprised at the result that I am getting. It seems that PDO::query generates prepared statements as well.
If you just need it once, then there's no point in creating a prepared statement (which unless emulated would result in two network transmissions to the database). Much less so when there are no variable parameters to be bound.
PDO::query is not about benefits. Its use comes with the absence of any. One-off queries don't benefit from the potential speed advantage of prepared statements.
I guess I have missed it completely. It states in the PHP manual for PDO::query that:
PDOStatement PDO::query ( string $statement )
Parameters
statement
The SQL statement to prepare and execute.
What this means is that the SQL statement is prepared even with PDO::query. Therefore there is absolutely no advantage to use PDO::query except saving a line or two on the PHP script. This is verified by the general query log shown in the question above.
Related
I'm working on an application at the moment that uses PDO with a MySQL database.
I'm seeing some queries, which are just very simple SELECT statements, e.g.
SELECT * FROM table ORDER BY name ASC
The code does not use prepare, for example:
$sql = "SELECT * FROM " . $this->table . " ORDER BY name ASC";
$stmt = $this->db->query($sql);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results;
Is it ok to do this, i.e. without using prepare, if there's no placeholders in the query?
The reason I've asked this is because according to the documentation it says
The SQL statement can contain zero or more named (:name) or question mark (?) parameter markers
which makes me wonder why you'd use this in the case of having no (zero) parameter markers?
Yes, because the use of prepared statements have 2 main causes:
Enhance running the same query with different parameters.
Prevent sql injection by separating sql code from the parameters.
Since you have no parameters that could be handled by a prepared statement (table names cannot be a parameter), you do not gain anything by pushing the query through as a prepared statement.
You still need to make sure that whatever is returned by $this->table will not cause any issues with the generated sql code.
Of course you can omit prepare if there is no dynamic data in your query.
But prepared statements have more advantages than only securing your queries.
According to http://php.net/manual/en/pdo.prepared-statements.php a second advantage is, that statements can be prepared once and executed multiple times.
The query only needs to be parsed (or prepared) once, but can be executed multiple times with the same or different parameters. When the query is prepared, the database will analyze, compile and optimize its plan for executing the query. For complex queries this process can take up enough time that it will noticeably slow down an application if there is a need to repeat the same query many times with different parameters. By using a prepared statement the application avoids repeating the analyze/compile/optimize cycle. This means that prepared statements use fewer resources and thus run faster.
Nevertheless, if you run your query only once and there is no dynamic data inside your query, omitting prepare is also fine.
In reality, you have to run such a query extremely seldom. A few tables with configuration-like options may be.
In all other cases at least LIMIT clause is obligatory.
So the question is rather a theoretical one.
The PHP manual states the following:
Calling PDO::prepare() and PDOStatement::execute() for statements that
will be issued multiple times with different parameter values
optimizes the performance of your application by allowing the driver
to negotiate client and/or server side caching of the query plan and
meta information, and helps to prevent SQL injection attacks by
eliminating the need to manually quote the parameters.
PHP PDO::prepare
For a single, once a page query - say:
$query = "SELECT id, content FROM pages WHERE id = :id LIMIT 1";
Is using prepare and execute like:
$statement = $connection->prepare( $query );
$statement->execute( array( ":id" => 5 ) );
The best way to go? I get a feeling from the manual that I should only be using prepare() for queries that will be bound multiple times. If this is the case, what are the other options? (Manually calling quote, and then PDO::query() possibly?)
I can't say if there are any performance implications when using prepared statements for a single query only.
Every query is parsed, analyzed and optimized. That also applies to PDO:query() (and mysql_*, mysqli_*, …). Prepared Statements - simply put - separate parsing, analyzing, optimizing and query-planning from executing the query. My (possibly naiive) assumption is that the only overhead prepared statmenets pose for a single query is the caching of the prepared results.
Using one API for executing queries should outweigh slight performance penalties, though. If you were to use PDO::query() instead of PDO::prepare() for single queries, you'd miss out on on the parameter binding and automatic escaping prepare() offers out of the box. This - potentially - opens a new can of worms, should you forget to PDO::quote() your data.
I only use PDO::query() for things that cannot be prepared in a reasonable fashion (e.g. "static" queries like SELECT foo FROM bar ORDER BY bla DESC LIMIT 1 and queries heavily using IN() et all).
Is there any point in saving PDO prepared statements for reuse in a session?
I'm building a site that uses MySQL fulltext queries, which I'm putting together in my PHP, like
SELECT * FROM table
WHERE MATCH (title) AGAINST ($search_string IN BOOLEAN MODE) AND
MATCH (keywords) AGAINST ($keywords IN BOOLEAN MODE)
ORDER BY $order_by $asc_desc
It seems to take a lot longer to run this kind of query when I prepare and execute it with bound parameters, than when I just prepare and execute a query string with the values included. But I need to use prepared statements to prevent the risk of SQL injection.
In any session I would very likely run the same query several times, with different parameter values. Would it make sense for me to save the PDOStatement object once it's been created (for example in the session)? If so, what would be the best way to do that? Would it be good practice to save each prepared statement in an associative array as it's created, with the SQL query string as the key for each?
On further reading I found you can't use bound params for the ORDER BY and ASC / DESC part of a statement. When I replace these with fixed values the performance improves.
There is no benefits to store prepare statement into session for reusable purpose,
the expensive cost is on the query execution itself.
If the data you are grabbing constantly changes, then caching the resultset may no longer be valid shortly after you retrieve them.
However, if your data appears to be static:
session_start();
$_SESSION['cachedResultSet'] = $fetchedResultset;
echo $_SESSION['cachedResultSet'][0][0];
# above statement `echo ...` depends on how your result is, be it an array or object
I think it would be acceptable to a degree to store resultsets in session variables, but perhaps it would depend (sorry to be redundant) whether the data changes quickly or on how often you grab this particular resultset, etc...
I have a query I would like to use that I would like to be reused for other select queries.
Is it possible to have a select query like this:
SELECT * FROM ? WHERE id = ?;
And then bind the values like this:
$stmt->bindValue(1, $table, PDO::PARAM_STR);
$stmt->bindValue(2, $id, PDO::PARAM_INT);
The problem is when I do this I get this $database->errorInfo() from a PDOException
HY000 1 near "?" syntax error.
I have tried taking out the table placeholder and it does work. Is it possible to do it my way or do I need to have separate functions?
Short answer: NO.
Long answer:
Refer to the PDO::prepare manual. There is a statement: This must be a valid SQL statement for the target database server. This means that your DB backend have to support prepared statement syntax that you use.
As far as I know, neither mysql, nor any other DB does not allow binding variables to occur in FROM clause. The reason for that lays deep in the concept of prepared statement. Prepared statement is being prepared inside the DB when you are calling prepare. This means that DB planner builds a plan for the query, so it can be executed multiple times with different parameters without building it again and again. To build a plan, planner needs to know affected tables, functions called, opportunities to use different fetch and join strategies (index scans/nested loops/etc.) and so on.
So, you cant 'bind' table name into prepared statement at the moment you want it to run, because DB needs table names at the moment when you prepare the statement. That's why you receive that message: DB requires all table names to be present in the preparing query.
When to close prepared statements in PHP?
Example:
$query = "insert into web_reviews (title,added_date,reviewer_home_url,read_more_link,summary) values(?,?,?,?,?)";
$stmt = $this->db->prepare($query);
$stmt->bind_params($this->title,$this->added_date,$this->reviewer_home_url,$this->read_more,$this->summary);
$stmt->execute() or die("Cannot add the date to the database, please try again.");
$stmt->close();
$stmt = $this->db->prepare("select id from web_reviews where title = ? and read_more = ?");
$stmt->bind_params($this->title,$this->read_more);
$stmt->execute();
$stmt->bind_results($web_review_id);
$stmt->close();
Should I use $stmt->close(); here?
Edit:
What is written on the PHP Manual and also one comment from the manual says:
Closes a prepared statement.
mysqli_stmt_close() also deallocates
the statement handle. If the current
statement has pending or unread
results, this function cancels them so
that the next query can be executed.
Comment:
if you are repeating an statement in
an loop using bind_param and so on
inside it for a larger operation. i
thougt id would be good to clean it
with stmt->close. but it broke always
with an error after aprox. 250
operations . As i tried it with
stmt->reset it worked for me.
That is a good use of close, especially since you are planning on making another query. With both PDO statements and MySQLi statements, I find that erring on the side of cleanliness is almost always for the best -- it removes potential bugs down the line.
As to the gentlemen with 250 operations... I don't see what the real use case is. Why does he need to query the database 250 different times? Why can't he query the database once with 250 records? Or, more likely, why can't he query the database 25 times with 10 records?
I am unable to comment currently, so I am just providing an answer. When you run a prepared statement that queries the database for a result, it will not execute another query unless you remove the current result it is storing. $result = $stmt->get_result().
Secondly, If you will need the result from the first query to be saved so that you use it later, then I recommend using two result sets. The first stores the result from the first execution of $stmt and the second for the second execution. This might not answer the question directly, but it may help someone.