get_posts()
を実行するときにmeta_query引数にエイリアスを設定する方法はありますか?私の質問の1つは、うまく機能していません。最適化するには、必要なときに3つのテーブルを結合するのではなく、同じ結合テーブルを再利用できるようにする必要があります。
私の現在の例...
$args = array(
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => array('puppy', 'kitten'),
'compare' => 'IN',
),
array(
'relation' => 'OR',
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'puppy',
'compare' => '=',
),
array(
'key' => 'abc_color',
'value' => 'pink',
'compare' => '=',
),
),
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'kitten',
'compare' => '=',
),
array(
'key' => 'abc_size',
'value' => 'large',
'compare' => '=',
),
),
),
)
);
get_posts($args);
これは基本的にストレートSQLでこれに変換されます...
SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
INNER JOIN postmeta AS mt3 ON ( posts.ID = mt3.post_id )
WHERE 1=1
AND
(
( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') )
AND
(
(
( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )
AND
( mt2.meta_key = 'abc_color' AND mt2.meta_value > 'pink' )
)
OR
(
( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' )
AND
( mt4.meta_key = 'abc_size' AND mt4.meta_value = 'large' )
)
)
) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;
ただし、これはカスタムメタフィールドabc_type
に2つの追加の結合を追加しているため、そのようなパフォーマンスに大きな影響を与えました。複数のmeta_query引数に対して同じエイリアスを参照できるようにする方法はありますか?基本的に、mt1
とmt3
は全く不要です。最初の( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') )
で使われる最初のpostmeta
テーブルを参照することができるはずです。または少なくともこれらのそれぞれにカスタムエイリアスを設定できる場合は、それを参照できます。
より最適なクエリは次のようになります。
SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
WHERE 1=1
AND
(
( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') )
AND
(
(
( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' )
AND
( mt1.meta_key = 'abc_color' AND mt1.meta_value > 'pink' )
)
OR
(
( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' )
AND
( mt2.meta_key = 'abc_color' AND mt2.meta_value = 'green' )
)
)
) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;
考えですか?
meta_query_find_compatible_table_alias
で定義されているwp-includes/class-wp-meta-query.php
フィルターを見てください。このフィルタのドキュメント:
/**
* Filters the table alias identified as compatible with the current clause.
*
* @since 4.1.0
*
* @param string|bool $alias Table alias, or false if none was found.
* @param array $clause First-order query clause.
* @param array $parent_query Parent of $clause.
* @param object $this WP_Meta_Query object.
*/
return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this );
呼び出し元の関数find_compatible_table_alias
がfalseを返しているため、クエリによってmt*
エイリアスが作成される可能性があります。このフィルタを使用したサンプルコードをいくつか示しますが、個人的にはもう少し理解しやすいものを推奨します。このようにクエリを変更すると、大量の問題が発生する可能性があります。特に、将来他の開発者を連れ込んだ場合は、クエリがめちゃくちゃになっている場所がまったく明らかにならない場合があります。それは言った...
// Reuse the same alias for the abc_type meta key.
function pets_modify_meta_query( $alias, $meta_query ) {
if ( 'abc_type' === $meta_query['key'] ) {
return 'mt1';
}
return $alias;
}
// Filter the query.
add_filter( 'meta_query_find_compatible_table_alias', 'pets_modify_meta_query', 10, 2 );
$args = array(
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => array('puppy', 'kitten'),
'compare' => 'IN',
),
array(
'relation' => 'OR',
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'puppy',
'compare' => '=',
),
array(
'key' => 'abc_color',
'value' => 'pink',
'compare' => '=',
),
),
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'kitten',
'compare' => '=',
),
array(
'key' => 'abc_size',
'value' => 'large',
'compare' => '=',
),
),
),
)
);
$q = new WP_Query($args);
echo '<pre>', print_r($q->request, true); die;
これは次のようなクエリになります。
SELECT SQL_CALC_FOUND_ROWS
wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )
WHERE
1=1
AND
(
( mt1.meta_key = 'abc_type' AND mt1.meta_value IN ('puppy','kitten') )
AND
(
(
( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )
AND
( wp_postmeta.meta_key = 'abc_color' AND wp_postmeta.meta_value = 'pink' )
)
OR
(
( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'kitten' )
AND
( mt1.meta_key = 'abc_size' AND mt1.meta_value = 'large' )
)
)
)
AND
wp_posts.post_type = 'post'
AND (
wp_posts.post_status = 'publish'
OR
wp_posts.post_status = 'future'
OR
wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
)
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
posts_where
およびposts_join
フィルタを使用してクエリを変更することができます。それほどエレガントではありませんが、あなたのSQLがより最適化されるように、あなたはこれら二つのフィルタを台無しにすることができるはずです。それは一種の強引な力ですが、WP_Queryクラスではもっと良い方法がわかりません。それがないと言っているのではありません。
//* Make sure to not suppress filters
$args = array(
'suppress_filters' => false,
//* rest of args unchanged
);
add_filter( 'posts_where', function( $sql ) {
$sql = str_replace(
"( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )",
"( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' )",
$sql
);
$sql = str_replace(
"( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' )",
"( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' )",
$sql
);
$sql = str_replace( [ 'mt2', 'mt4' ], [ 'mt1', 'mt2' ], $sql );
return $sql;
});
add_filter( 'posts_join', function( $sql ) {
$sql = str_replace(
" INNER JOIN wp_postmeta AS mt4 ON ( wp_posts.ID = mt4.post_id )",
"",
$sql
);
$sql = str_replace(
" INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id )",
"",
$sql
);
return $sql;
});
誤って他のクエリを変更しないように、おそらくそこにいくつかのチェックがあるはずです。それは読者のための課題として残されています。
次のように、最初のメタクエリは冗長なので削除することでクエリを最適化できます。
$args = array(
'meta_query' => array(
'relation' => 'OR',
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'puppy',
'compare' => '=',
),
array(
'key' => 'abc_color',
'value' => 'pink',
'compare' => '=',
),
),
array(
'relation' => 'AND',
array(
'key' => 'abc_type',
'value' => 'kitten',
'compare' => '=',
),
array(
'key' => 'abc_size',
'value' => 'large',
'compare' => '=',
),
),
),
);
get_posts($args);
このようにしてあなたはpink puppy
かlarge kitten
のどちらかしか得られないでしょう。
WordPressのMySQL内部クエリを最適化することに関しては、副作用に晒される可能性があるので、それを避けてください。あなたは、クエリがキャッシュされているという事実に頼ることをお勧めします。そして(より大きな)データセットに対してPHP処理をもう少し行います。ボトルネックはデータベースから抽出するデータ量ではなく、収集するのが困難であるため(クエリ数)、全体的にパフォーマンスが向上すると考えられます。 PHPは配列を通過するのと同じくらい高速です。
そのため、投稿メタがキャッシュされることを考えると、このような状況はより速いと思います。
$args = array(
'meta_query' => array(
array(
'key' => 'abc_type',
'value' => array('puppy', 'kitten'),
'compare' => 'IN',
),
),
);
$final_posts = array();
foreach( $get_posts($args) as $post ) {
if ( 'puppy' === get_post_meta( $post->ID, 'abc_type', true ) ) {
if ( 'pink' === get_post_meta( $post->ID, 'abc_color', true ) ) {
$final_posts[] = $post;
}
} else {
// This is definitely a kitten
if ( 'large' === get_post_meta( $post->ID, 'abc_size', true ) ) {
$final_posts[] = $post;
}
}
}