本文へ移動

トラブルシュート

WP_Queryでランダム表示するときの注意点
|orderby=randの負荷も確認

関連記事やおすすめ記事を更新ごとに入れ替えたいなら、WP_Queryのorderby=randを使えます。ただし、ページ送りでは同じ記事が再登場し、大量の候補では重くなることがあります。まず「毎回変えるか、一定時間固定するか」を決め、少数件で記事IDを比較します。

Huddly Huddly 投稿日: 2026年10月7日 読了目安: 7分

毎回変えるか、一定時間固定するかを先に決める

おすすめ枠は更新ごとに変えたいですか? それとも、1時間や1日は同じ内容でよいでしょうか。前者ならWP_Queryのorderby => ‘rand’を使えます。後者なら、選んだ投稿IDを一定時間保存するほうが目的に合います。

この記事では、少数のおすすめ枠を前提に、対象の絞り方、ページ間や別枠との重複、負荷、固定方法を確認します。ページネーション付きのメイン一覧や、大量の候補へ無条件に使う方法ではありません。

更新で順番が変わるのは正常、重複・遅さは別に調べる

randは毎回必ず違う結果を返す保証ではありませんが、再取得で選ばれる記事や順番が変わるのは通常の動きです。 ページ間や別枠での重複、表示の遅さ、一定時間固定したいのに毎回変わる状態は分けて調べます。

症状よくある原因最初に見る場所
ページ更新ごとに順番が変わるorderby=rand による通常の動き$args の orderby
2ページ目に同じ記事が出るランダム順とページネーションの相性が悪いpaged / paginate_links 周辺
表示が重い大量投稿に対して毎回ランダム取得しているposts_per_page / 絞り込み条件
別枠と同じ記事が出る除外IDを指定していないpost__not_in
ランダムなのに順番を固定したいランダム結果を保存していないtransient / post__in

まずは最小のランダム表示で確認する

まず、公開済みの通常投稿から3件を表示します。クラシック子テーマのtemplate-parts/random-posts.phpへ保存し、表示したいテンプレートのメインループ内でthe_post()の後にget_template_part( ‘template-parts/random-posts’ );を呼んでください。投稿本文やfunctions.phpの直下へ貼るコードではありません。通常投稿以外ならpost_typeを登録キーへ置き換えます。

確認用として追加した場合は、終了後に呼び出しとファイルを撤去します。後半の固定版と同じ場所で同時に呼ぶと、別々の一覧が出るので、どちらか1つを使います。

現在のWP Query Builderでは、詳細設定で「post / publish / 3」、orderbyにrand、ignore_sticky_postsにtrueを入力し、自動高速化はオフにしました。次の$argsは、その実画面で生成された内容を変更せずに使っています。

クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。

PHPtemplate-parts/random-posts.php通常の投稿からランダムに3件だけ表示する最小例
<?php$args = array(    'post_type' => 'post',    'post_status' => 'publish',    'posts_per_page' => 3,    'orderby' => 'rand',    'ignore_sticky_posts' => true); $random_query = new WP_Query( $args ); if ( $random_query->have_posts() ) {   echo '<ul>';    while ( $random_query->have_posts() ) {      $random_query->the_post();       printf(         '<li><a href="%1$s">%2$s</a> / ID: %3$d</li>',         esc_url( get_permalink() ),         esc_html( get_the_title() ),         get_the_ID()      );   }    echo '</ul>';} else {   echo '<p>表示できる投稿はありませんでした。</p>';} wp_reset_postdata();

Builderが生成したのは$argsまでです。その後のnew WP_Query()、リンクと投稿IDの表示、0件時の文言、wp_reset_postdata()は、結果を確かめるために手動で追加しています。

orderby => ‘rand’で並べ、検証中は記事IDも表示します。これで似たタイトルではなく、同じ投稿が重複したかを判断できます。

同じ候補から取得した2回の表示を比べる

次の2枚は、公開済みの通常投稿23件を候補に、本文コードで3件ずつ表示したページです。1回目と再読み込み後の2回目で、選ばれた投稿IDを見比べます。各回の一覧内に同じIDが2つ出ることと、前の回にも出たIDが再登場することは別です。

ランダムに選ばれたID62・60・57の3件が並ぶ1回目のおすすめ一覧
1回目の表示記事IDを記録してから再読み込みする

候補は公開済みの通常投稿23件、取得は最大3件。この回はID62・60・57の順でした。

同じ23件の候補から選んだID57・62・14が並ぶ2回目のおすすめ一覧
再読み込み後選ばれた記事と順番を見比べる

2回目はID57・62・14。ID57と62は再登場しています。条件は同じでも、毎回違う3件になる保証はありません。

更新前後の表示と負荷は、本番と分けた環境で確かめます。サーバー上でも試す場所が必要なら、XserverのPHP対応やバックアップ条件を確認できます。

よくある原因と確認順

2ページ目以降で順番が崩れる場合は、ページネーションの確認順へ進みます。別の表示枠と同じ記事が混ざる場合は、重複表示の原因を確認してください。投稿数が多いサイトではWP_Queryの高速化も合わせて確認してください。

件数、重複、ページネーション、候補集合の大きさ、固定化の順で確認します。

ランダム表示の確認順

  1. 1
    まず3件程度の最小クエリで orderby => 'rand' が効くか確認する
  2. 2
    タイトルだけでなく記事IDも表示して重複を確認する
  3. 3
    ページネーション付き一覧に使っていないか確認する
  4. 4
    大量投稿を毎回ランダム取得していないか確認する
  5. 5
    順番を一定時間固定したい場合はIDリストの保存を検討する

1. 更新するたびに順番が変わる

orderby => ‘rand’ を使っている場合、ページを更新するたびに順番が変わることは不自然ではありません。ランダム表示は、毎回同じ順番を保証するための指定ではないからです。

「今日だけ同じ順番にしたい」「1時間は同じおすすめ記事を出したい」という要件があるなら、毎回ランダム取得するのではなく、ランダムに選んだ記事IDのリストを一定時間保存する設計を考えます。

ページキャッシュやCDNが同じHTMLを返している間は、randを指定していても更新のたびに変わらないことがあります。 ログイン中と未ログイン時で結果が違う場合は、クエリだけでなくキャッシュの有効範囲も確認してください。すぐに表示を変えたいときは、対象ページのキャッシュを削除するか、キャッシュの保存時間と除外設定を見直します。サイト全体のキャッシュを無条件に無効化する必要はありません。

2. ページネーションと組み合わせている

ランダム表示とページネーションは相性に注意が必要です。1ページ目を開いた時と2ページ目を開いた時でランダム順が再計算されると、前のページに出た記事が次のページにも出たり、逆に見逃す記事が出たりします。

ページネーション付きのアーカイブでは、基本的には日付順、更新日順、ID順、メニュー順など、順番が安定する指定を使います。 どうしてもランダム順でページを分けたい場合は、先にランダムなIDリストを作って固定し、その順番に沿ってページ分割する設計が必要です。

たとえば候補をID10〜15の6件に絞り、posts_per_pageは3のまま、pagedを1・2にして別々に取得すると、ある回は1ページ目が10・11・12、2ページ目が14・15・12でした。ID12が重複し、ID13を見られない組み合わせです。これは再計算による一例で、常に同じIDが重複するわけではありません。

3. 表示件数だけを減らして安心している

ランダム表示は、少数の記事を見せる用途では便利ですが、posts_per_page => 3なら常に軽いわけではありません。返す投稿が3件でも、候補集合が大きければ、その候補をランダムに並べる処理は重くなり得ます。

表示数だけでなく、投稿タイプ、カテゴリー、タクソノミー、候補IDで母集団も絞ります。 「全投稿から3件」と「特定カテゴリーの50件から3件」では、同じ表示件数でも条件が違います。共通の上限値はないため、投稿数、呼び出し頻度、同時アクセスを検証環境で計測して判断します。meta_queryやtax_query、posts_per_page => -1との組み合わせは、より慎重に確認してください。

4. 別の表示枠と記事が重複している

現在の記事や先に表示した記事を除外するには、そのIDをpost__not_inへ渡します。次は通常投稿の個別ページ用の差分です。最初の$args定義直後、$random_query = new WP_Query( $args );より前へ置き、表示ループはそのまま使います。

引数や処理の一部分だけを示した例です。このまま単独で貼らず、テンプレート内のWP_Queryとループへ組み込んでください。

PHPrandom-posts-exclude-args.php通常投稿の個別ページで現在の記事を候補から外す差分
if ( is_singular( 'post' ) ) {   $args['post__not_in'] = [ get_queried_object_id() ];}

カテゴリーのアーカイブではget_queried_object_id()が投稿IDではなくタームIDになるため、個別投稿かを判定しています。先に表示した枠も除外するなら、その枠で取得した投稿IDを同じ除外配列へ追加します。

大量のIDをpost__not_inへ渡すと別の負荷が増えます。現在の記事や先に表示した数件程度に留め、大きな除外リストが必要なら候補集合を見直します。post__inで候補を限定している場合はpost__not_inを同時指定せず、先に候補配列から除外IDを取り除いてください。残りが空ならクエリを実行しない分岐も必要です。

5. 固定表示投稿の扱いを見落としている

通常の投稿一覧では、固定表示投稿が先頭に入ることがあります。ランダム表示の検証中に「なぜか同じ記事が目立つ」と感じる場合は、固定表示投稿の影響も確認してください。

ランダム枠で固定表示投稿を先頭へ移動したくない場合は、ignore_sticky_posts => trueを指定します。これは表示要件の指定であり、性能対策ではありません。また、固定表示投稿そのものを候補から除外する指定でもありません。完全に除外したい場合は、get_option( ‘sticky_posts’ )で得たIDをpost__not_inへ渡します。

候補の範囲と除外の方針を決めたら、WP Query Builderでランダム取得の$argsを組み立てられます。生成した条件を基本版の表示ループへ渡し、投稿IDを確認します。

少数のランダム枠の条件を作る

post_type、posts_per_page、orderbyから取得条件を作れます。選んだIDの保存・再利用やキャッシュの更新処理は、後半のコードで別に実装します。

選んだIDを最大1時間保存して再利用する

一定時間は同じおすすめを見せるなら、最初に選んだID列をTransientへ保存し、その順で再取得します。Transient APIは、計算結果などを有効期限付きで一時保存するWordPressの仕組みです。

次はtemplate-parts/random-posts-fixed.phpへ保存し、基本版の呼び出しをget_template_part( ‘template-parts/random-posts-fixed’ );へ交換する例です。条件は「公開済みの通常投稿から3件」で、同じキーを使う表示枠・閲覧者で結果を共有します。現在の記事の除外や、閲覧者ごとの選出は含んでいません。

最初のID取得は、Builderで基本版の条件にfields: idsとno_found_rows: trueを加え、自動高速化をオフにして生成しました。保存済みIDを動的に渡す2回目の取得、Transientの読込み・保存・削除、表示ループは手動で組み合わせています。

クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。

PHPtemplate-parts/random-posts-fixed.phpランダムに選んだIDを最大1時間保存し、公開状態を確かめて同じ順で表示する
<?php$cache_key = 'wpq_random_post_ids_post_3_v1';$post_ids  = get_transient( $cache_key ); if ( false === $post_ids ) {   $args = array(       'post_type' => 'post',       'post_status' => 'publish',       'posts_per_page' => 3,       'orderby' => 'rand',       'no_found_rows' => true,       'ignore_sticky_posts' => true,       'fields' => 'ids'   );    $seed_query = new WP_Query( $args );    $post_ids = array_map( 'absint', $seed_query->posts );    if ( $post_ids ) {      set_transient( $cache_key, $post_ids, HOUR_IN_SECONDS );   }} if ( $post_ids ) {   $random_query = new WP_Query( [      'post_type'           => 'post',      'post_status'         => 'publish',      'post__in'            => $post_ids,      'orderby'             => 'post__in',      'posts_per_page'      => count( $post_ids ),      'ignore_sticky_posts' => true,   ] );    if ( $random_query->have_posts() ) {      echo '<ul>';       while ( $random_query->have_posts() ) {         $random_query->the_post();          printf(            '<li><a href="%s">%s</a> / ID: %d</li>',            esc_url( get_permalink() ),            esc_html( get_the_title() ),            get_the_ID()         );      }       echo '</ul>';   } else {      echo '<p>表示できる投稿はありませんでした。</p>';   }    if ( $random_query->post_count < count( $post_ids ) ) {      delete_transient( $cache_key );   }    wp_reset_postdata();} else {   echo '<p>表示できる投稿はありませんでした。</p>';}

最初のクエリはfields => ‘ids’でIDだけを選びます。再表示ではpost__inとorderby => ‘post__in’を組み合わせ、保存順を維持します。空のpost__inは広い結果になることがあるため、IDが空ならクエリを実行しません。

保存中の投稿が削除・非公開化されると、その投稿は再取得で外れます。表示件数が保存ID数より少ない場合はキーを削除し、次の実行で選び直します。この実行中に不足分を補充するコードではありません。ページキャッシュが古いHTMLを返す場合の更新は別に必要です。

実務上の補足

Transientの有効期限は「その時間まで必ず残る」という保証ではなく、保持される最大時間です。早く消えてもIDを再生成できるようにします。キーには投稿タイプ・分類・件数など条件を識別できる情報を含め、投稿の削除・非公開化・条件変更時にはTransientを削除または更新してください。

この例を撤去する時は、呼び出しとファイルを外し、自分の環境で作ったwpq_random_post_ids_post_3_v1だけをdelete_transient()で削除します。サイト全体のキャッシュを消す必要はありません。

保存中・消失後・0件で固定版の動きを比べる

同じ23件を候補にした固定版では、最初に選ばれたID15・79・55が、次のリクエストでも同じ順で表示されました。保存がなくなった後は、再び選出処理へ進みます。

状態表示されたID動き
保存なしで初回表示15・79・55ランダム選出し、有効期限3600秒で保存
保存ありで再表示15・79・55ランダム選出をせず保存順で取得
保存を削除して再表示56・27・61再びランダム選出する
保存IDが存在しない投稿のみ0件0件メッセージを出し、保存を削除
候補投稿が0件0件空のpost__inでは取得せず、0件メッセージを出す

ここで比べたのは別リクエストでの再利用と、保存が途中で消えた場合の動きです。1時間ずっと保持されることや、ページキャッシュ・CDNの更新まで保証する例ではありません。

避けたい使い方

一覧全体、検索結果、カテゴリー、ページネーション付き画面では、読者が同じ結果を辿りにくくなります。関連記事の発見や古い記事の再露出など、用途を少数枠へ限定します。

orderby => ‘rand’は表示順をランダムにするための機能です。公平な抽選、セキュリティ用トークン、A/Bテストの固定割り当て、均等なローテーションには使わないでください。これらには再現性や割り当てを管理できる専用の仕組みが必要です。

ランダム表示とキャッシュの公式資料

ランダム順と取得条件はWP_Query、別ループ後の復元はwp_reset_postdata()、一定時間の固定はTransient APIの公式資料で確認します。

公式リンク

公式ドキュメント

ランダム表示の最終判断

  • ランダム表示が本当に必要か、更新順や固定選出で代替できないか判断した
  • ページ間の重複を許容するか決めた
  • 対象件数と除外する投稿IDを先に絞った
  • 順番を一定時間保つ場合の保存期間と更新契機を決めた
  • 同じ記事の再表示をタイトルではなく投稿IDで確認した
WP Query BuilderBuilderで条件を組み直す条件を選び、WP_Queryのコードを画面で作れます。Builderでクエリを作る

この記事を書いた人

Huddly

Web制作 / フロントエンドエンジニア

WordPressとフロントエンド実装で迷いやすい点を、公式情報と検証結果を分けながら、初学者にも追いやすい順序で整理しています。

この記事の検証情報

検証環境

WordPress
7.1.4
PHP
8.3.36
テーマ
wpq-media + DDEV
対象
カスタム投稿 / taxonomy / meta_query

更新履歴

  • 2026年10月7日 初回公開

この記事を活用する

あとで見返す・共有する

お気に入りに保存できます。役に立ったポイントは、いいねや共有で教えてください。

任意のご支援 役に立ったときだけ、活動を応援できます OFUSEはサイト・記事制作に使います。支援の有無で、読める内容や使える機能は変わりません。