ポストメタで投稿を取得する最も効率的な方法


36

メタデータを含む多数の投稿を取得する必要があります。もちろん、標準の投稿クエリを使用してメタデータを取得することはできないため、通常は投稿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:それは価値がありますか?どのように多くのpostmeta2-クエリアプローチが良好となる前に、私は追加しないテーブルを結合?あるクエリですべての投稿データを取得してから、関連するすべてのポストメタを別のクエリで取得し、PHPで1つの結果セットでメタと投稿データを結合することができます。それが可能であれば、それは単一のかつてないほど複雑なSQLクエリよりも速くなるでしょうか?

私はいつも「できる限り多くの作業をデータベースに与える」と考えています。これはわかりません!


参加したいかどうかもわかりません。get_posts()とget_post_meta()の組み合わせにより、同じデータが返されます。実際、後で使用しないデータを取得する可能性があるため、結合の使用は効率的ではありません。
rexposadas

2
とにかく、メタデータが自動的にキャッシュされないのですか?
マニーフルーモンド

私は戻ってくる数百の記事を持っている場合@rxn、確かにそれはかなり重いのDB負荷がするのです、(彼らは、カスタムポストタイプをしている)get_posts()、その後、get_post_meta()それらの一つ一つのために?@ MannyFleurmond、WPのビルトインキャッシングに関するハード情報を見つけることは困難ですが、リクエストごとにキャッシュすることはわかっています。このデータを取得するためのサーバーへの呼び出しはAJAX呼び出しであり、他のものがその前に何かを取得することはないと思います。
スティーブテイラー

実際、複数のクエリを実行し、結果をキャッシュします。複数の値を持つフィールドを含む投稿メタが必要なだけでなく、メタフィールド(これらの2つのセット)を介して投稿に接続しているユーザーのデータと、それらのユーザーメタも必要です。純粋なSQLは間違いなく範囲外です!
スティーブテイラー

回答:


58

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

したがって、このための独自のクエリを作成しないでください。

通常のクエリに対するメタキャッシングの仕組み:

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

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

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

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

つまり、適切なクエリと単純な永続メモリソリューションを使用するよりも、クエリのロードとロードが遅くなる可能性があります。通常のを使用しますWP_Query。労力を節約してください。

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

追加の追加:「データベースにできるだけ多くの作業を与えます。」...いいえ、これはWebです。異なるルールが適用されます。一般に、可能であれば、データベースにできる限り少ない作業を与えたいと常に思っています。データベースの処理速度が遅いか、構成が不適切です(特に構成しなかった場合、これが真実であるとお金をかけることができます)。多くの場合、それらは多くのサイト間で共有され、ある程度オーバーロードされます。通常、データベースよりも多くのWebサーバーがあります。一般に、DBから必要なデータをできる限り高速かつ簡単に取得し、Webサーバー側のコードを使用してデータを整理します。もちろん、一般的な原則として、さまざまなケースはすべて異なっています。


30

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

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

この回答には正しいマークを付ける必要があります。
ルーク

データベースクエリを探している場合、これは正しい答えです
アレックスポポフ

このクエリは、WP_Queryを使用していたときの時間を約25秒から約3秒に短縮しました。私の要件は、これを一度だけ実行することでしたので、キャッシュは必要ありませんでした。
クッシュ

11

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

Ottoの提案を使用して試してみました-すべての投稿に対してWP_Query :: queryを実行し、次に各投稿に対してループしてget_post_customを実行します。 これには、平均で約3秒かかりました

次に、イーサンのピボットクエリを試しました(興味のある各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の時間であり、ピボットクエリソリューションの約半分です。

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


永続オブジェクトキャッシュソリューションでどのような結果が得られるか興味があります。データベースと構成によっては、基本ケースではオブジェクトキャッシュが遅くなる場合がありますが、ホストの大部分での実際の結果では、結果が大きく異なります。メモリベースのキャッシングは途方もなく高速です。
オットー

おい@オットー。データを取得するために使用する方法に関係なく、結果をキャッシュすることは間違いありません。一時的なAPIを使用して試しましたが、メモリの問題が発生しています。2000個のオブジェクトのシリアル化された文字列が〜8Mでクロックし、set_transient()が失敗します(メモリ不足)。また、max_allowed_pa​​cket MySQL設定を変更する必要があります。ファイルにキャッシュすることを検討しますが、そのパフォーマンスについてはまだわかりません。要求間で持続するメモリにキャッシュする方法はありますか?
トレバーミルズ

はい、永続的なメモリキャッシュ(XCache、memcached、APCなど)があり、オブジェクトキャッシュプラグイン(W3 Total Cacheは多くのタイプのメモリキャッシュをサポートします)を使用する場合、すべてのオブジェクトキャッシュをメモリに保存します。ほとんどすべての倍の高速化。
オットー

バックボーン/アンダースコアjsフィルタリングスキームで使用する6000アイテムを返します。これは、タイムアウトしたためWP_Queryとして実行することさえできなかった6秒のカスタムクエリを受け取り、2秒のクエリにしました。array_mapをは...戻ってかなりダウンして減速するものの
ジェイク

WP_Query内のすべてのメタデータを返すための高性能サポートを構築するためのサポートはありますか?
atwellpub

2

最終的にCSVドキュメントを作成するためにこのタスクを実行する必要がある状況にあることに気付いたため、最終的にmysqlで直接作業することになりました。私のコードはpostおよびmetaテーブルを結合してwoocommerceの価格情報を取得します。以前に投稿されたソリューションでは、正しく機能するために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 +行を作成したため、非常に大きく、非常に低速でした。


1

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);

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

注: 投稿から他のデータ(タイトル、コンテンツなど)実際に必要ない場合は、2つだけ実行できます :-)


0

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);

-1

多値メタフィールドの問題にも出くわしました。問題はWordPress自体にあります。wp-includes / meta.phpを見てください。この行を探してください:

$where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value );

問題はCASTステートメントにあります。メタ値のクエリでは、$ meta_type変数はCHARに設定されます。CHARへの値のキャストがシリアル化された文字列にどのように影響するかについての詳細はわかりませんが、修正するには、キャストを削除してSQLが次のようになるようにします。

$where[$k] = ' (' . $where[$k] . $wpdb->prepare( "$alias.meta_value {$meta_compare} {$meta_compare_string})", $meta_value );

今では、それは機能しますが、WordPressの内部をいじっているので、他のことが壊れる可能性があり、WordPressをアップグレードする必要があると仮定した場合、永続的な修正ではありません。

私が修正した方法は、必要なメタクエリ用にWordPressで生成されたSQLをコピーし、探しているmeta_valuesの余分なANDステートメントを追加するPHPを書いて、$ wpdb-> get_results($ sql )最終出力用。ハッキーですが、動作します。


試したことはありませんが、get_meta_sqlコアコードをハッキングするよりも、この行に続くフィルターを活用する方が望ましいでしょう。
スティーブテイラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.