WordPress

Wordpress meta_query でカスタムフィールドの値が無い投稿を取得するときにハマったこと

問題

WP_Query
を使って カスタムフィールドの値がゼロ または 値が設定されていない 投稿を取得したかったけど、何度 meta_query を見直しても取得できなかった

動かなかった $args

$args = [
    'post_type' => 'custom_post_type',
    'post_status' => 'publish',
    'meta_query' => [
        'relation' => 'AND',
        [
            'key' => 'custom_field_1',
            'value' => 'A',
        ],
        [
            'relation' => 'OR',
            [
                'key' => 'custom_field_target',
                'value' => '0',
            ],
            [
                'key' => 'custom_field_target',
                'compare' => 'NOT EXISTS',
            ],
        ]
    ],
    'orderby' => 'meta_value',
    'meta_key' => 'custom_field_target',
    'order' => 'ASC',
    'posts_per_page' => -1,
];

これの何がいけないかわかります…?

解決

    'orderby' => 'meta_value',
    'meta_key' => 'custom_field_target',

これが悪さをしていました。実装上、不要な orderby なので削除したら動くように。

原因

生成されたクエリを読んでいると、 'orderby' => 'meta_value' が指定されていると、

SELECT 
  wp_posts.* 
FROM 
  wp_posts 
  /* ↓ meta_value で orderby するための JOIN */
  LEFT JOIN wp_postmeta ON (
    wp_posts.ID = wp_postmeta.post_id
  ) 
  /* ↓ meta_query のための JOIN */
  LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
/*...中略...*/
WHERE 
  1 = 1 
  AND (
    wp_postmeta.meta_key = 'custom_field_target' /* !! */ 
    AND (
        /* ここに meta_query で指定した条件 */
    )
 )
/*...略...*/

…というクエリになるので、NOT EXISTSを使っても無意味になる、と。