Get all WooCommerce customers paid orders with a SQL query

lufizi picture lufizi · Oct 4, 2017 · Viewed 10.5k times · Source

I need a query that returns me all the users with orders with payment approved.

I'm having a hard time finding where in the db this is stored. I found only post_status 'wc-complete', but I don't think this is the right info.

   SELECT a.post_status, b.meta_value FROM wp_posts a, wp_postmeta b
   WHERE a.ID = b.post_id 
   AND a.post_type = 'shop_order' 
   AND a.post_status = 'wc-completed'

Answer

LoicTheAztec picture LoicTheAztec · Oct 4, 2017

Using "completed" order status for paid orders is correct

Here is a custom function with a SQL query, that will output an formatted array of User IDs with their completed orders (paid / accepted):

function completed_orders_ids_by_costumer_id(){

    global $wpdb;

    $query = $wpdb->get_results("

        SELECT pm.meta_value AS user_id, pm.post_id AS order_id
        FROM {$wpdb->prefix}postmeta AS pm
        LEFT JOIN {$wpdb->prefix}posts AS p
        ON pm.post_id = p.ID
        WHERE p.post_type = 'shop_order'
        AND p.post_status = 'wc-completed'
        AND pm.meta_key = '_customer_user'
        ORDER BY pm.meta_value ASC, pm.post_id DESC
    ");

    // We format the array by user ID
    foreach($query as $result)
        $results[$result->user_id][] = $result->order_id;

    return $results;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Tested and works.


EXAMPLE USAGE just for test to see the raw outputted data

echo '<pre>'; print_r(completed_orders_ids_by_costumer_id()); echo '</pre>';

You will get something like:

Array
(
    [9] => Array
        (
            [0] => 505
            [0] => 497
        )

    [12] => Array
        (
            [0] => 626
            [1] => 584
            [2] => 483
        )

    [15] => Array
        (
            [0] => 614
            [1] => 598
        )

    [17] => Array
        (
            [0] => 634
        )

    … / … and so on …

)

Now in wp_postmeta table you will also some other meta keys related to paid orders that you could use:
'_paid_date'
'_date_paid'
'_date_completed'
'_completed_date'

But targeting "completed" order status is just fine

Using that others listed meta keys, you can make your SQL query (that can replace our first query in the function):

$query = $wpdb->get_results("
    SELECT DISTINCT pm.meta_value AS user_id, pm.post_id AS order_id
    FROM {$wpdb->prefix}postmeta AS pm
    LEFT JOIN {$wpdb->prefix}posts AS p
    ON pm.post_id = p.ID
    LEFT JOIN {$wpdb->prefix}postmeta AS pm2
    ON p.ID = pm2.post_id
    WHERE p.post_type = 'shop_order'
    AND pm.meta_key = '_customer_user'
    AND pm2.meta_key IN ( '_paid_date', '_paid_date', '_date_completed', '_completed_date' )
    AND pm2.meta_value != ''
    ORDER BY pm.meta_value ASC, pm.post_id DESC
");

Tested and works too…