I am learning WP_query and recently ran into a problem.

I need to do a query where i first filter trough one or more custom fields, which works great thanks to meta_query.

However, i also need it to order by multiple custom fields.

(Like in sql you can write "ORDER BY field ASC, field2 DESC")

This is what i have so far:

    'post_type' => 'personer',
    'meta_query' => array(
            'key' => 'p_seller',
            'value' => 'Y',
            'compare' => '='
    'meta_key' => 'p_region',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'post_status' => 'publish',
    'posts_per_page' => 999,
    'caller_get_posts'=> 1

It returns rows with p_seller set to Y and order by p_region. I'd also like to add a second orderby which order them by p_lastname after region. Or even better - p_region, p_lastname, p_firstname. All ascending, but i'd love to know how to combine ASC and DESC aswell :)

I have done some googling, but most people seem to write about multiple fields for filtering, which i got. Or they do space out in custom SQL queries, which i hope i can get by without? :)

4 Answers 4

$qry_args = array(
    'post_status' => 'publish', 
    'post_type' => 'event', // Post type
    'posts_per_page' => -1, // ALL posts

    'orderby' => 'meta_value',
    'meta_key' => 'year',
    'order' => 'DESC'


add_filter( 'posts_orderby', 'filter_query' );
$all_posts = new WP_Query( $qry_args );
remove_filter( 'posts_orderby', 'filter_query' );

function filter_query( $query ) {
    $query .= ', post_modified DESC';
    return $query;

Here is an example that works. The code above orders all posts of type event: first DESCENDENT by year, second DESCENDENT by modified date for posts from same year.

without the second ordering they would remain ordered ASCENDENT if they had the same year.

p.s. year is a custom field added by me to posts of type "Event"


Use named queries, example from https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters

$q = new WP_Query( array(
'meta_query' => array(
    'relation' => 'AND',
    'state_clause' => array(
        'key' => 'state',
        'value' => 'Wisconsin',
    'city_clause' => array(
        'key' => 'city',
        'compare' => 'EXISTS',
'orderby' => array( 
    'city_clause' => 'ASC',
    'state_clause' => 'DESC',

) );

  • WP_Query has come a long way since i originally posted this question :)
    – jonas
    Commented Jun 12, 2018 at 16:53

You should customize the query via posts_orderby filter

In your template

add_filter('posts_orderby', 'custom_orderby');
$query= new WP_Query($args);
remove_filter('posts_orderby', 'custom_orderby');

In your functions.php

function custom_orderby($orderby) {
    return "ORDER BY field ASC, field2 DESC";
  • Hmm, did not work.. seems kind of weird that it should since that seems like targeting regular sql fields but i guess custom fields are not even in the posts table in the database?
    – jonas
    Commented Oct 31, 2012 at 15:29
  • no custom fields are not in the same table, they are in the wp_postmeta table. There are filters available for customizing the full query(near line 2600 in wp-includes/query.php). Use posts_clauses to var_dump the query that is being used. It just needs a little tweaking. Commented Oct 31, 2012 at 19:04
  • Sorry but if anyone could give me a working example to reverse engineer it would help a lot!
    – jonas
    Commented Nov 6, 2012 at 8:14

I am sure there are more elegant solutions, but this is what i came up with and it works for now...

global $wpdb;

$querystr = "
    SELECT wposts.*
    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta, $wpdb->postmeta wpostmeta2, $wpdb->postmeta wpostmeta3
    WHERE wposts.ID = wpostmeta.post_id
    AND wposts.ID = wpostmeta2.post_id
    AND wposts.ID = wpostmeta3.post_id
    AND wpostmeta.meta_key = 'p_seller'
    AND wpostmeta2.meta_key = 'p_role'
    AND wpostmeta3.meta_key = 'p_firstname'
    AND wpostmeta.meta_value = 'N'
    AND wposts.post_type = 'personer'
    AND wposts.post_status = 'publish'
    ORDER BY wpostmeta2.meta_value ASC, wpostmeta3.meta_value ASC

$pageposts = $wpdb->get_results($querystr);

if ($pageposts){
    foreach ($pageposts as $post){

        // the_title() or whatever works here..              

  • I agree plain SQL does the trick. I would use aliases for comprehension.
    – sglessard
    Commented Feb 19, 2014 at 3:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.