If you design or develop WordPress themes or plugins, there’s a good chance that someday you’ll need to make a query for custom meta fields. These are those completely custom key/value pairs that you can attach to any post, page, or custom post type. WordPress has a basic UI for them by default, or you can use something like Advanced Custom Fields to get fancy with them. But under the hood ACF uses regular ol’ custom fields.
This very snippet page you are looking at right now was written in 1999. At that time, in order to query for posts with particular custom fields, you would need to use the `$wpdb` global variable. That can be used for creating MySQL queries that the WordPress WP_Query() class doesn’t support. Fortunately today, WordPress does have arguments that support queries for custom meta fields.
Here, we’ll cover the different ways you can request and loop over posts with particular custom fields (and their values). You’ll be able to use this information whether you use the WP_Query
class, query_posts()
, or get_posts()
. Since query_posts()
and get_posts()
are wrappers for the WP_Query
class. They all accept the same arguments.
The Query Arguments
Here is a basic example of a WordPress query taken from the WordPress Codex.
<?php
// The Query
$the_query = new WP_Query( $args );
// The Loop
if ( $the_query->have_posts() ) {
echo '<ul>';
while ( $the_query->have_posts() ) {
$the_query->the_post();
echo '<li>' . get_the_title() . '</li>';
}
echo '</ul>';
} else {
// no posts found
}
/* Restore original Post Data */
wp_reset_postdata();
The $args
is the important bit there. We’ll be passing different arguments to make this work how we want.
When querying for custom meta, there are two “groups” of arguments that you can use. One group is for a simple custom meta field query and the other group of for more complex custom meta fields queries. Let’s start with the simple group.
meta_key
The meta_key
argument will query any post that has the custom field meta ID saved to the database, whether or not there is a value saved for the field. The meta_key
is the ID that you give to your meta fields. Like this:

This example will query any post that has the custom meta field with the ID of “field1”.
$args = array( 'meta_key' => 'field1' );
meta_value
The meta_value
argument queries post that have the value you define. The meta_value
argument is used for string values. This example will query any posts with a custom meta field that has the value “data1”.
$args = array( 'meta_value' => 'data1' );
You can also combine the two. This example will only query posts that have the custom meta field with the ID of “field1” that has the value of “data1”.
$args = array(
'meta_key' => 'field1',
'meta_value' => 'data1'
);
meta_value_num
The meta_value_num argument is similar to the `meta_value` argument. Where the meta_value
argument is ment for string values the meta_value_num
is meant for numerical values.
This example shows how to query the “field1” custom meta field if it has a value of “10”.
$args = array(
'meta_key' => 'field1',
'meta_value_num' => '10',
);
meta_compare
The meta_compare
argument does exactly what it sounds like. It’ll allow you to use comparators with the `meta_value` and `meta_value_num` arguments. The comparators you can use are ‘=’, ‘!=’, ‘>’, ‘>=’, ‘<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'NOT EXISTS', 'REGEXP', 'NOT REGEXP' or 'RLIKE'.
Here's an example that shows how to query any posts that don't have the value of "data1".
$args = array(
'meta_key' => 'field1',
'meta_value' => 'data1',
'meta_compare' => '!=',
);
More Complex Queries
meta_query
The main argument you’ll use for complex queries is meta_query
. This argument on it’s own doesn’t do anything. It just tells WordPress that you want to make a query for custom meta fields. You’ll add additional arguments within meta_query
that will be used to define the query.
key, value, and compare
The arguments key
, value
work exactly the same way as meta-key
, meta-value
as described above. The complex compare
is similar to the simple compare
above, but it take a different list of comparators. The complex compare
uses ‘=’, ‘!=’, ‘>’, ‘>=’, ‘<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'EXISTS', or 'NOT EXISTS'.
value
can be an array, but only when compare is using ‘IN’, ‘NOT IN’, ‘BETWEEN’, or ‘NOT BETWEEN’.
If you use ‘EXISTS’, or ‘NOT EXISTS’ with compare
, you do not need to specify a value
argument.
Here is an example that will query posts if it has “field1” with the value “data1”, and “field2” with the value that is not “data2”.
$args = array(
'meta_query' => array(
array(
'key' => 'field1',
'value' => 'data1'
),
array(
'key' => 'field2',
'value' => 'data2',
'compare' => '!=',
)
)
);
relation
The relation
is used when you want to query custom meta data using a logical relationship. You can use AND
or OR
. For example you’ll use AND
to compare if data1 and data2 meet the criteria, and you use OR
if data1 or data2 meet the criteria.
This argument is stand-alone. Meaning it doesn’t appear in a individual custom meta field parameters. Let’s look at an example. This example will only query posts that have “field1” with the value of “data1”, and “field2” with the value of “data2”.
$args = array(
'meta_query' => array(
'relation' => 'AND'
array(
'key' => 'field1',
'value' => 'data1',
),
array(
'key' => 'field2',
'value' => 'data2',
),
)
);
If you changed relation
to “OR”. Then it would query any posts if “field1” has the value of “data1”, or if “field2” has the value of “data2”.
type
The type
argument allows you to choose the type of data to query. You can use ‘NUMERIC’, ‘BINARY’, ‘CHAR’, ‘DATE’, ‘DATETIME’, ‘DECIMAL’, ‘SIGNED’, ‘TIME’, or ‘UNSIGNED’.
The “DATE” type can be used with the compare
“BETWEEN” only if the date format is “YYYYMMDD”.
This example will query any post where the value of “field1” is numeric.
$args = array(
'meta_query' => array(
array(
'key' => 'field1',
'value' => 'data1',
'type' => 'NUMERIC'
)
)
);
Real World Example
So far, I’ve only given examples with arbitrary data and fields. Now, I’d like to show you a real world example of querying custom meta fields.
The Scenario
You’ve created an events custom post type. The events post type has a date custom field with the ID of event_date
. You want to create a query that will show any events that will be starting on the current date through the next 30 days.
We’re going to use the meta_query
argument as we want to use the type
argument to define the “event_date” field as “DATE” data type.
This is the query:
$args = array(
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'event_date',
'value' => array( date( 'Ymd', strtotime( '-1 day' ) ), date( 'Ymd', strtotime( '+31 days' ) ) ),
'compare' => 'BETWEEN',
'type' => 'DATE'
)
)
);
$event_query = new WP_Query( $args );
The value
is an array of the current date – 1 day and 31 days from the current date. Since we’re using the comparator “BETWEEN” only the posts between the value array will be queried, so we want to offset them by one day.
With this query you’ll display any event occurring in the next 30 days.
Conclusion
The WP_Query
class is a very flexible class that will allow you to create a multitude of custom queries. If you want to learn more about the different arguments you can use for queries I recommend looking through the WP_Query
codex page.
This is great! What if you want to limit the loop to 5 posts/pages?
Okay, that was easy. Just added LIMIT 4 to the query details…
hmm..is it support wordpress 2.9???
Great stuff, using it in several places in one of my wp templates.
In addition to this, is there a way to query mulitiple custom fields and print the “merged” results?
Isn’t this the same result you’d obtain using wp_query?
Something like this:
http://wordpress.pastebin.com/uJDdsiCw
I used this reference:
http://codex.wordpress.org/Template_Tags/query_posts#Custom_Field_Parameters
but haven’t tried it yet.
It actually can. I just used it that way.
I couldnt implement the code in the article whether for noobness or didnt understand where to put it.
I got your code and integrated it with a simpler query;
$loop = new WP_Query( $args );
if($loop->have_posts()): while ( $loop->have_posts() ) : $loop->the_post();
Here’s an easier way to accomplish this task. http://pastie.org/2464333 It works like a charm every time!
The flexibility of using SQL query is more than custom post queries. For example if you want to search through a range or something like that.
I’ve been trying to figure out, how to show posts from a specific category, if the page template being viewed has a custom field value with the same name.
ie. a custom field value is present, and posts in a category with the same name as that custom field value are shown.
I am trying to use this to output child pages of a certain parent page that have a particular custom field. Problem is it spits out more than one of the same child page depending on how many values have been declared for the custom field. That is if the child page has two different costume field VALUES for “soundfile” it will output that childpage twice. How can I limit that. Here is the link to the page I am working on:
http://www.oliviablock.net/listen/
Thanks in advance
OK, by some freakin miricle I was able to figure out the solution to the above comment. Instead of OBJECT I used OBJECT_K which results output as an associative array of row objects, using first column’s values as keys so that duplicates where discarded.
Thanks,
carol
Thanks for this Chris!!! You just saved my life with this one! :)
Fantastic! This worked like a charm for me, thanks so much!
Very nice Chris. This gives me the control I was looking for for out putting post content on my home page. Cheers mate.
I wanted to thank you for this super useful post. It basically saved my life. I have spent so much time trying to do something similar with the wordpress built in queries and not been able to.
Thank you !
Really good post Chris, I almost thought it would solve a problem I’m having, which I’ve looked everywhere for info on but couldn’t find a solution. I’m hoping someone here will have some idea of how to do this or if it’s even possible.
Below is part of the code from this post, my custom field is called ‘views’ and by using the wp-postviews plugin, that custom field is given the number of how many people have viewed that post. Is there any way to get the last line, instead of ordering the posts by date, to order them by the custom field view number values, in descending order? I’ve all but given up on trying, so if anyone knows I’d be very grateful!
$querydetails = ”
SELECT wposts.*
FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
WHERE wposts.ID = wpostmeta.post_id
AND wpostmeta.meta_key = ‘views’
AND wposts.post_status = ‘publish’
AND wposts.post_type = ‘post’
ORDER BY wposts.post_date DESC
Just to update, someone else was kind enough to update the current code I had (not the one above) to order posts by view count. Just thought I’d share it below incase anybody is looking to do the same thing.
This is a great post!
With this script, based on custom fields, WordPress is more flexible.
Thank you! You’ve saved my work. :)
I know this is a year old post, but I have been searching for a solution to my problem and I have yet to find a solution.
Code wise, I am using exactly what you have posted. However, the query only retrieves one result.
Any ideas what might cause this?
Thanks in advance!
i am unable to change the loop query in the
code
how can i modify
it by custom fields ..
and query
please any one help me ….
Actually i have ti implement the where and like conditions in the query.
for the searching by a particular values
Well indeed an old post but I would to implement the same query but with multiple conditions.somthing like this:
You were looking for this trick.
Hi Chris
I was just about to use this code when I spotted what I think to be a tiny error.
On the line:
I think there is one too many ‘>’ after the first PHP segment. The div is being ended too soon.
Great post! Solved custom fields sql problem.
Thanks!
THANKS.. GREATT TUT REALLY HELPED.
Works great, thanks so much! :-)
Works great :) thanks for sharing it
brilliant… (so is a lot of the design on this site, i might add!)
one question… is there a “wildcard” option for wpostmeta.meta_value ?
basically if there’s NO data in the meta_key i’ve selected, i want to hide the loop, but as long as there’s SOMETHING in it, i’d like it to appear…
there are too many variables (and more added on occasion) to make it viable to have to hard code an array…
i’ve tried $key and that works as long as there is a value, but when there is no value it actually displays the query output (with no data) twice…
I’m trying to use this to create a custom calendar, but because I’m very new at this, I am kinda lost. I know that I need to use the above code to pull out the meta data for the date. But the posting of the title and permalink within the specific date of the calendar is beyond me. I know that I can pull the title and permalink and display as above, but how can I get it to display in the correct date box for the calendar? Anyone got any ideas?
PS: Like I said above, I’m very new at this, so I’m trying to figure things out as I go. Please excuse my ignorance.
why not using this snippet?
Am going to have to use this for a really weird archive layout I’ve been asked to build; does anyone know how I’d go about using WordPress’ inbuilt pagination with these results? I know I can use OFFSET and LIMIT in the SQL nad do it manually, but I’d ideally like to hook into the default WordPress implementation if possible.
Good stuff! Been using it for a bunch of queries! Thanks.
hi all.
i need to check two meta keys and meta values. if any of the meta key and meta value is find then get the post and the related meta data.
like
where ( wpostmeta.meta_key = ‘any’
AND wpostmeta.meta_value = ‘any’)
Or
( wpostmeta.meta_key = ‘another’
AND wpostmeta.meta_value = ‘another’),
please help, and sorry for weak english.
hi all.
i need to check two meta keys and meta values. if any of the meta key and meta value is find then get the post and the related meta data.
like
Or
please help, and sorry for weak english.
For those who don’t want to mess with the code, there is a very easy solution though the Advanced Post Types Order plugin more details at http://www.nsp-code.com/how-to-order-wordpress-posts-using-a-custom-field-value/<a/>
Question about this. I understand that when using wp_query the metadata and other goodies are cached. Do you lose those speed benefits with this query?
Now i am able to write custom queries to show post meta data which i wanted in my custom loop. You are Awesome :)
Some good tips here but I am trying to get the custom field to be equal to the page title and that way I can have many pages using the same loop.
Thank you for sharing of this snippet! You made my day!
Thanks , works perfect ! :)
Amazingly, the MIN() & GROUP BY works and I was able to get the results I wanted via the “SQL” below, but am having some trouble.
Now I want to access that single MIN(pm.meta_value) as date per p.ID. That’s a vital piece of information and I don’t know how to get that returned in the resulting loop.
Hi, good stuff. Interesting article but I think for the sake of this article you must use the GROUP BY clause not to get duplicate rows.
It would be also useful to add pagination stuff due to the way you query using SQL and not WP_Query.
I think this should work better:
SELECT wposts.*
FROM posts wposts,postmeta wpostmeta
WHERE wposts.ID = wpostmeta.post_id
AND wposts.post_status = ‘publish’
AND wposts.post_type = ‘post’
GROUP BY wposts.ID
ORDER BY wposts.post_date DESC;
I’ve used this on http://www.glocalmart.it where I just try to fit the search form needs by querying w/o using WP_Query.
Thank you.
This should REALLY redirect to post v.3.2 standards, do you have a tutorial on meta queries?
http://codex.wordpress.org/Class_Reference/WP_Meta_Query
This whole thing should be done through the query system, not a custom select statement.
I am having issue showing Custom fields in a genesis custom template and can’t figure it out!
I want to be able to style the fields and I am using custom classes to also style the layout!
I can not for the life of me figure out how to add these fields to the content area. I was able to just add these using php in genesis before but it will not work now with execute-pro theme.
here is the code I want to add to the content area on my page template. http://codepen.io/anon/pen/RPGqNb.html
Great article, just one observation;
relation’ => ‘AND’
Shouldn’t there be a comma after the closing ‘? Without it, it kills the site?
Simon is correct. I ran into the same problem. There should be a comma or it causes a syntax error. Otherwise, great stuff. Very helpful.
Cool information. Very useful to beginners
Hi i want to fire query like this is it possible. since date in table is string format “20 January 2016” like this and i want to filter the date based on date so is it possible please let me know.
$query_args[‘meta_query’][] = array(
‘DATE(key)’ => ‘_start_date’,
‘value’ => array($end_date, $start_date),
‘compare’ => ‘BETWEEN’,
‘type’ => ‘date’,
);
OR
$query_args[‘meta_query’][] = array(
‘key’ => ‘DATE(_start_date)’,
‘value’ => array($end_date, $start_date),
‘compare’ => ‘BETWEEN’,
‘type’ => ‘date’,
);
Thank you.
Great article. Clear, succinct and very helpful. Tnx!!
Hi Chris,
Great post! I’m trying to query posts from the post type Product that match with the current post in the loop from the post type Item, based on the custom fields product-ean and item-ean.
For example: i have a Item post with item-ean 123 and 2 Product posts with product-ean 123. Now i would like to show both Product posts on the Item post.
Help is greatly appreciated, thank you.