When we first had Standard accepted into the WordPress.com marketplace, one of the number one priorities that the Theme Team suggested was to use WP_Query in place of raw queries or in place of query_posts.
And for the work we were doing, they were right.
For what it’s worth, I’m all for using WP_Query. Generally speaking, if there’s an API available that allows me to interact with the data layer rather than using raw queries, I am for it regardless of the framework. That’s exactly the case with WP_Query.
Straight from the Codex:
The wp-blog-header.php (or the WP class in Version 2.0) gives the $wp_query object information defining the current request, and then $wp_query determines what type of query it’s dealing with (possibly a category archive, dated archive, feed, or search), and fetches the requested posts. It retains a lot of information on the request, which can be pulled at a later date.
Sounds awesome, right? And it is.
But in all of the WordPress work I’ve done over the past couple years, I had to resort to using a raw query in a recent project and had a decent case for when not to use WP_Query.
Or so I thought.
What’s the problem?
In this particular project (that I’ll cover in more detail in a few days), I’m storing a single piece of post meta data. Strictly speaking, I’m storing a unique key with the ID of a single category.
The sample code looks like this:
add_post_meta( $post_id, 'sticky_key', $category->cat_ID );
Simple enough.
On the first page of any archive index, I needed to pull back the post associated with this meta data regardless of where it falls in the list of archived posts.
But trying something such as:
$sticky_query = new WP_Query( 'orderby=date&order=asc&meta_key=sticky_key&meta_value=' . get_query_var( 'cat' ) );
Didn’t work. Instead, it lead to the following

In case it’s unclear, that’s PHP memory allocation error:
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 523800 bytes)
Ew.
Granted, I could increase the amount of memory allocated to WordPress, but this isn’t something that’s necessarily easy to do across the shared web hosts so it’s not really a feasible option.
So what’s left to do?
After all was said and done – and this includes trying various actions, filters, storing it creatively in private attributes, etc., you name it – I ended up falling back to $wpdb and doing a direct query to pull back just the post ID.
Basically:
global $wpdb; $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s ORDER BY meta_id ASC LIMIT 1", 'sticky_post', $category->cat_ID ) );
And it works and it works fast. Then again, there’s very little overhead when trying to pull back a single value from the database, right?
As Konstantin points out in the comments, this query still isn’t optimal – though it retrieves the post idea for the conditions, it doesn’t discriminate against post drafts, password protected posts, and so on. Thus, you’d technically be left with a post ID being returned that may compromise what the user was actually hoping to display.
So what’s the resulting call to WP_Query that I ended up using?
$sticky_query = new WP_Query( array( 'fields' => 'ids', 'post_type' => 'post', 'posts_per_page' => '1', 'meta_query' => array( array( 'key' => 'sticky_post', 'value' => $category->cat_ID, ) ) ) );
And a h/t to Kovshenin for the gist.
Raw Queries To The Rescue?
So here’s the thing: Attempting to pull back a single value from the database feels more like a job for $wpdb than using WP_Query.
But after a conversation that went from the comments into Skype, it’s more of a matter of truly understanding WP_Query and a deeper level – territory I’m still definitely still exploring.
All that to say, we often hear to “WP_Query all the things!” and until recently, I was skeptical. It seemed that the API was better suited to some situations rather than others.
But that’s not necessarily accurate.
Definitely feel free to share any additional thoughts or questions on WP_Query - surely I’m not the only one who could benefit from all of this
.








Hi Tom!
Looks like you missed a couple of things
First of all, what you’re trying to do looks like a perfect fit for taxonomies, but not knowing the context behind all that, I might be wrong. In any case, here’s why your custom SQL query is wrong.
You’re selecting from the post meta table, which lacks the overall context about your post, meaning it will not only select published, public posts, but can easily lead to revisions, drafts, attachments, menus, pages, future, private and password protected posts, as well as custom post types (that may be private overall) — basically anything that can contain your sticky_post key. You must JOIN on the posts table to give your query that context.
Your WP_Query approach can benefit from several things. A posts_per_page argument, if you’d like to select only one such post. A fields set to ids, if you’d like to select post IDs only. However, if you’re going to fetch and print out the retrieved post, you might as well get it all in one go instead of firing two (or more) separate queries. Here’s the modified query, along with the resulting SQL, which looks slightly more secure than yours: https://gist.github.com/6301b8c3e0af3fcc8e8c There is no way such a query can result in anywhere near 30 megs of memory usage in php
So before blaming WP_Query, spend some time playing around with it, and make sure you understand the resulting queries from your requests. Use the SAVEQUERIES constant in your wp-config.php along with the Debug Bar plugin to inspect your queries. Don’t forget to run EXPLAIN on the queries to figure out what exactly MySQL is doing, and do try and run them in content-heavy environment.
Hope that helps. Cheers, and good luck!
Konstantin
We talked about this about Skype a bit this morning, but because of the nuances of custom taxonomies in the .com and .org arenas, I’ll be sticking with post meta.
That said, you’re right about the SQL: it doesn’t discriminate against any of the post statuses – so, yeah, there’s a security risk there (more for, say, password protected posts), but it’s still just a query for retrieving an ID and this wouldn’t actually cause any exploit issues. Plus, in the context of the plugin I’m working on, all it’d do would be to highlight a password protected post – it wouldn’t reveal anything
.
All that said, your advice on
WP_Queryis spot on. You hit on exactly the snag I was missing. I’ve updated the code that’ll eventually ship with the plugin and I’ve made sure to revise the post to highlight the discussion we had here an on Skype.Thanks for the heads up – always appreciate it!