web-dev-qa-db-ja.com

Postmetaで投稿する最も効率的な方法

私はそれらのメタデータを含むたくさんの投稿を取得する必要があります。もちろん、標準の投稿クエリでメタデータを取得することはできません。そのため、通常は投稿ごとにget_post_custom()を実行する必要があります。

私はこのように1つのカスタムクエリを試しています。

$results = $wpdb->get_results("
    SELECT  p.ID,
        p.post_title,
        pm1.meta_value AS first_field,
        pm2.meta_value AS second_field,
        pm3.meta_value AS third_field
    FROM    $wpdb->posts p LEFT JOIN $wpdb->postmeta pm1 ON (
            pm1.post_id = p.ID  AND
            pm1.meta_key    = 'first_field_key'
        ) LEFT JOIN $wpdb->postmeta pm2 ON (
            pm2.post_id = p.ID  AND
            pm2.meta_key    = 'second_field_key'
        ) LEFT JOIN $wpdb->postmeta pm3 ON (
            pm3.post_id = p.ID  AND
            pm3.meta_key    = 'third_field_key'
        )
    WHERE   post_status = 'publish'
");

動作しているようです。同じ投稿で複数のメタ値が許可されるような方法でこれらのメタフィールドのいずれかを使用すると、失敗します。それを実現するための結合は考えられません。

質問1:複数値のメタフィールドを取り込むための結合、サブクエリなどがありますか。

しかし質問2:それは価値がありますか? 2クエリのアプローチが望ましいようになるまでにpostmetaテーブルのジョインをいくつ追加するのですか? 1つのクエリですべての投稿データを取得し、次に別のクエリで関連するすべてのpostmetaを取得し、1つの結果セットでメタデータと投稿データを組み合わせることができます。可能であれば、これは1つの複雑さが増したSQLクエリよりも速くなるでしょうか。

私はいつも「データベースにできる限りの作業をする」と考えています。よくわからない!

33
Steve Taylor

ポストメタ情報は、WP_Queryパラメータを使用して明示的に指定しない限り、標準のupdate_post_meta_cache(およびメインクエリ)用にメモリに自動的にキャッシュされます。

したがって、あなたはこれのためにあなた自身の質問を書くべきではありません。

メタキャッシュが通常のクエリに対してどのように機能するか

update_post_meta_cacheWP_Queryパラメータがfalseに設定されていない場合は、投稿がDBから取得された後にupdate_post_caches()関数が呼び出され、それがupdate_postmeta_cache()を呼び出します。

update_postmeta_cache()関数はupdate_meta_cache()のラッパーで、基本的に検索された投稿のすべてのIDを持つ単純なSELECTを呼び出します。これにより、クエリ内のすべての投稿についてすべてのpostmetaが取得され、そのデータがオブジェクトキャッシュに保存されます(wp_cache_add()を使用)。

get_post_custom()のようなことをするとき、それは最初にそのオブジェクトキャッシュをチェックします。したがって、現時点ではポストメタを取得するために余分なクエリを作成することはありません。投稿をWP_Queryで取得した場合、メタは既にメモリ内にあり、そこから直接取得されます。

ここでの利点は、複雑なクエリを作成するよりも何倍も大きいことですが、最大の利点はオブジェクトキャッシュを使用することです。 XCache、memcached、APCなどの永続的なメモリキャッシュソリューションを使用していて、オブジェクトキャッシュをそれに結び付けることができるプラグイン(たとえば、W3 Total Cache)がある場合、オブジェクトキャッシュ全体は高速メモリに格納されます。既に。その場合、データを取得するのに必要な zero クエリがあります。それはすでに記憶にあります。永続オブジェクトキャッシングは多くの点で素晴​​らしいです。

言い換えれば、あなたのクエリはおそらく適切なクエリと単純な永続的メモリソリューションを使用するよりもロードとロードが遅くなります。通常のWP_Queryを使用してください。あなた自身をいくらか努力してください。

追加: update_meta_cache()は賢い、ところで。メタ情報がすでにキャッシュされている投稿のメタ情報は取得されません。基本的に同じメタを2回取得することはありません。超効率的です。

追加の追加情報: "データベースに可能な限り多くの作業をしてください。" ...いいえ、これはWebです。異なる規則が適用されます。一般的には、可能であれば、データベースには可能な限り little workを指定します。データベースの処理が遅い、または適切に設定されていない(具体的に設定していない場合は、これが当てはまることを賭けることができます)。多くの場合、それらは多くのサイトで共有されており、ある程度過負荷になっています。通常、データベースよりも多くのWebサーバーがあります。一般的には、必要なデータをできるだけ速く簡単にDBから取得してから、Webサーバー側のコードを使用してそれを選別します。一般原則として、もちろん、異なるケースはすべて異なります。

56
Otto

ピボットクエリをお勧めします。あなたの例を使用して:

SELECT  p.ID,   
        p.post_title, 
        MAX(CASE WHEN wp_postmeta.meta_key = 'first_field' then wp_postmeta.meta_value ELSE NULL END) as first_field,
        MAX(CASE WHEN wp_postmeta.meta_key = 'second_field' then wp_postmeta.meta_value ELSE NULL END) as second_field,
        MAX(CASE WHEN wp_postmeta.meta_key = 'third_field' then wp_postmeta.meta_value ELSE NULL END) as third_field,

 FROM    wp_posts p LEFT JOIN wp_postmeta pm1 ON ( pm1.post_id = p.ID)                      
GROUP BY
   wp_posts.ID,wp_posts.post_title
27
Ethan Seifert

関連するメタ情報を含む多数の投稿をすばやく検索したい場合もあります。 O(2000)件の投稿を取得する必要があります。

私はOttoの提案を使って試した - すべての投稿に対してWP_Query :: queryを実行し、そして各投稿に対してget_post_customをループして実行する これは完了するのに平均で約3秒かかった

それから私はEthanのピボットクエリを試しました(私が興味を持っていた各meta_keyを手動で要求する必要はありませんでしたが)。 meta_valueのシリアル化を解除するために、取得したすべての投稿をループしなければなりませんでした。 これは完了するのに平均で約1.3秒かかった

次にGROUP_CONCAT関数を使用してみたところ、最良の結果が見つかりました。これがコードです:

global $wpdb;
$wpdb->query('SET SESSION group_concat_max_len = 10000'); // necessary to get more than 1024 characters in the GROUP_CONCAT columns below
$query = "
    SELECT p.*, 
    GROUP_CONCAT(pm.meta_key ORDER BY pm.meta_key DESC SEPARATOR '||') as meta_keys, 
    GROUP_CONCAT(pm.meta_value ORDER BY pm.meta_key DESC SEPARATOR '||') as meta_values 
    FROM $wpdb->posts p 
    LEFT JOIN $wpdb->postmeta pm on pm.post_id = p.ID 
    WHERE p.post_type = 'product' and p.post_status = 'publish' 
    GROUP BY p.ID
";

$products = $wpdb->get_results($query);

// massages the products to have a member ->meta with the unserialized values as expected
function massage($a){
    $a->meta = array_combine(explode('||',$a->meta_keys),array_map('maybe_unserialize',explode('||',$a->meta_values)));
    unset($a->meta_keys);
    unset($a->meta_values);
    return $a;
}

$products = array_map('massage',$products);

これは平均0.7秒かかった 。これは、WP get_post_custom()ソリューションの約4分の1、ピボットクエリソリューションの約半分です。

たぶんこれは誰かに興味があるでしょう。

8
Trevor Mills

私は最終的にそこからCSVドキュメントを作成するためにこのタスクを実行する必要があるという状況に自分自身を見つけました、私はこれをするためにmysqlと直接働くことになった。私のコードは、投稿とメタテーブルを結合して、ウーコマースの価格設定情報を取得します。以前に投稿されたソリューションでは、SQLでテーブルエイリアスを使用して正しく機能することが必要でした。

SELECT p.ID, p.post_title, 
    MAX(CASE WHEN pm1.meta_key = '_price' then pm1.meta_value ELSE NULL END) as price,
    MAX(CASE WHEN pm1.meta_key = '_regular_price' then pm1.meta_value ELSE NULL END) as regular_price,
    MAX(CASE WHEN pm1.meta_key = '_sale_price' then pm1.meta_value ELSE NULL END) as sale_price,
    MAX(CASE WHEN pm1.meta_key = '_sku' then pm1.meta_value ELSE NULL END) as sku
    FROM wp_posts p LEFT JOIN wp_postmeta pm1 ON ( pm1.post_id = p.ID)                 
    WHERE p.post_type in('product', 'product_variation') AND p.post_status = 'publish'
    GROUP BY p.ID, p.post_title

しかし、警告されないでください、woocommerceは私のメタテーブルに300K +行を作成したので、それは非常に大きく、それゆえ非常に遅くなりました。

2
Terry Kernan

いいえSQLのバージョン:

SQLなしですべての投稿とそのすべてのメタ値(メタ)を取得します。

たとえば、投稿IDのリストがIDの配列として格納されているとしましょう。

$post_ids_list = [584, 21, 1, 4, ...];

1つのクエリですべての投稿とすべてのメタを取得することは、少なくとも少しのSQLを使用しないと不可能なので、2つのクエリを実行する必要があります(まだ2つだけ)。

1.( WP_Query を使用して)すべての投稿を取得する

$request = new WP Query([
  'post__in' => $post_ids_list,
  'ignore_sticky_posts' => true, //if you want to ignore the "stickiness"
]);

"ループ" のあとに wp_reset_postdata(); を呼び出すことを忘れないでください。)

2.メタキャッシュを更新する

//don't be confused here: "post" means content type (post X user X ...), NOT post type ;)
update_meta_cache('post', $post_ids_list);

メタデータを取得するには、@/Attoが指摘したように、標準の get_post_meta() を使用するだけです。
最初にキャッシュを調べます:)

注:あなたが実際に投稿からの他のデータ(タイトル、内容など)を必要としない場合)あなたはただ2をすることができます。 :-)

1
jave.web

ソリューション形式trevorを使用して、ネストしたSQLで機能するように変更します。これはテストされていません。

global $wpdb;
$query = "
    SELECT p.*, (select pm.* From $wpdb->postmeta AS pm WHERE pm.post_id = p.ID)
    FROM $wpdb->posts p 
    WHERE p.post_type = 'product' and p.post_status = 'publish' 
";
$products = $wpdb->get_results($query);
0