Core WordPress functions and classes are generally excellent regarding to performance and security.
When it comes to retrieving data from the database, the WP_Query
class and its methods provide us with a safe and efficient way to do it, avoiding complex SQL queries. There are some additional wrappers of WP_Query
, like query_posts()
and get_posts()
.
query_posts
WordPress Codex explicitly says “Its overly-simplistic approach to modifying the main query can be problematic and should be avoided wherever possible“.
This query wrapper is inefficient (re-runs SQL queries) and will fail in some circumstances, especially when dealing with posts pagination. It instantiates a new WP_Query object and reassigns that to the global wp_query
, breaking the main query that many plugins, themes and custom scripts rely on. For secondary queries (custom loops) instantiate your own WP_Query.
Use new WP_Query
or get_posts()
which are pretty much interchangeable (latter is thin wrapper for former). If you do want to alter the main query, then use the pre_get_posts
hook with $query->is_main_query()
check. pre_get_posts
is a filter for altering any query and it is most often used to alter the ‘main query’. This is the trac about the deprecation of query_posts
.
Therefore:
You should never use
query_posts()
.
get_posts
This is essentially a wrapper for a separate instance of a WP_Query
object. This isn’t a ‘Loop’, it simply returns an array of post objects.
It accepts the same arguments as WP_Query, doesn’t modify global variables and is safe to use anywhere. It passes 'no_found_rows' => true
by default so it is used for non paginated queries only. It ignores sticky posts, needs just a foreach loop to display the $post property and requires a setup_postdata( $post )
to make the template tags available. WP_Query
uses the loop and template tags are available by default.
Use
get_posts()
for some quick, non-paginated, data fetch.
WP_Query
WP_Query
is the class behind both the above functions. The power of WP_Query
derives from the possibility to create and work with your own instance of it. It is more of a general purpose tool, similar to directly writing MySQL, for a detailed custom query script.
A bit more complex with fewer restrictions, thus making it more powerful while being safe to use anywhere.
Use a
new WP_Query
for an in-depth, flexible query script.
Reset/Cleanup
- Use
wp_reset_query()
if you’ve usedquery_posts()
or altered global$wp_query
directly – hopefully you will never need to. - Use
wp_reset_postdata()
if you’ve usedthe_post()
orsetup_postdata()
to restore initial state of global $post.
Performance
All queries are eventually wrappers of WP_Query
, and while some are better than other (never use query_posts
), the performance differences of get_posts
vs WP_Query
are negligible. The WP_Query
is faster than get_posts
by a very small margin.
However parameters provided by the WP_Query
class can specifically optimize SQL queries, reducing execution time and resource consumption. (Ref. 1 | 2)
- When we don’t need pagination, we should set
no_found_rows
totrue
, making the query run dramatically faster. - When specific fields are not required, limit the returned fields to IDs:
'fields' => 'ids'