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? :)

$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"


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 Feb 19 '14 at 3:35

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 Jun 12 '18 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 Oct 31 '12 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. – Mridul Aggarwal Oct 31 '12 at 19:04
  • Sorry but if anyone could give me a working example to reverse engineer it would help a lot! – jonas Nov 6 '12 at 8:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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