WordPress多种分类法混合查询优化的一种方案

在使用wordpress建站,特别是一些CMS类型的站点,多种分类法是常用的,然而wp在使用多种分类法混合查询的时候,这个查询语句就复杂了,在分类和文章数量多了之后,速度就堪忧了,所以这里提一种优化方案供参考。

举例:某个案例中,对于默认的除了的category和post_tag,新增了一个名为kind的分类法,注册新分类法的步骤这里就不赘述了,然后我们需要查询出同时归属于categoryID为1,kind的ID为2的文章,普通的做法:

$args = array( 
  'post_type'=>'post',
  'tax_query'=>array(
    'relation'=>'AND',
    array(
      'taxonomy'=>'category',
      'field'=>'term_id',
      'operator'=>'IN',
      'terms'=>array(1)
    ),
    array(
      'taxonomy'=>'kind',
      'field'=>'term_id',
      'operator'=>'IN',
      'terms'=>array(2),
    )
  )
);
query_posts($args);

就上面这个查询来说,在没有缓存的情况下要执行3次数据库查询,前面两次分别查询对应的分类是否存在,重点是第3次文章查询,由于wordpress分类和文章的对应关系存储wp_term_relationships表中,每篇文章对应每一个分类都有一条记录,所以查询文章的时候主要要两次left join这个wp_term_relationships表,一旦数据量多起来,这样的查询就比较慢了,附打印出的查询语句如下。

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)  LEFT JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id) WHERE 1=1  AND ( 
  wp_term_relationships.term_taxonomy_id IN (1) 
  AND 
  tt1.term_taxonomy_id IN (2)
) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

我的解决方案,在wp_posts表中增加一个字段,用来存储器中的一个分类的信息,比如这个案例里面我把kind的ID信息冗余存储在wp_posts表中,然后将查询条件放到wp_posts表中,这样就能提速了。

第一步:在启用主题的时候,执行sql在wp_posts表中增加kind_id字段,并加上Index索引。(在这个案例中因为客户的分类可以确定是单选,所以kind_id字段的类型就直接用int了,如果你要保存多个分类ID,可以改为varchar类型,不过varchar类型就不别弄索引了)。

//激活主题时执行
function ashu_add_pages() {
  global $pagenow;
  if ( 'themes.php' == $pagenow && isset( $_GET['activated'] ) ){
    ashuwp_alter_posts_table();
  }
}
add_action( 'load-themes.php', 'ashu_add_pages' );

//修改posts表,增加一列kind_id
function ashuwp_alter_posts_table(){
  global $wpdb;

  //判断字段是否已经存在
  $sql1 = "Describe {$wpdb->posts} `kind_id`";
  $kind_id_exist = $wpdb->query($sql1);

  if( !$kind_id_exist ){
    //新增列
    $add_column = "ALTER TABLE {$wpdb->posts} ADD COLUMN `kind_id` INT(10) DEFAULT NULL";
    $wpdb->query($add_column);

    //添加索引
    $add_index = "ALTER TABLE {$wpdb->posts} ADD INDEX kind_id (`kind_id`)";
    $wpdb->query($add_index);

  }

}

第二步:发布文章的时候,将kind分类的ID信息保存到wp_posts表中的kind_id字段。这里使用set_object_terms钩子,就是在设置分类的时候用,当然为了避免后台多选,这里只保存一个ID数据

add_action( 'set_object_terms', 'ashuwp_add_post_kind_id', 10, 6);
function ashuwp_add_post_kind_id( $object_id, $terms, $tt_ids, $taxonomy, $append = false, $old_tt_ids = array() ){
  
  global $wpdb;
  if( $taxonomy=='kind' ){
    $term_id = reset( $tt_ids );
    if( $term_id ){
      $sql = "UPDATE {$wpdb->posts} SET `kind_id`={$term_id}  where `ID`={$object_id}";
      $wpdb->get_results( $sql );
    }
  }

}

第三步:我希望在使用WP_Query查询文章时直接传入kind_id参数即可,所以使用posts_where钩子,检测是否有kind_id参数,然后拼接查询语句。

function ashuwp_query_posts_where( $where, $query){
  global $wpdb;

  $qv = $query->query_vars;

  isset( $qv['kind_id'] ) AND $kind_id = absint($qv['kind_id']) AND $where .= " AND {$wpdb->posts}.kind_id = {$kind_id}";

  return $where;

}
add_filter( 'posts_where', 'ashuwp_query_posts_where', 10, 2);

第四步:查询文章。最开始那一段查询可以直接改成如下代码即可。

$args = array( 
  'post_type'=>'post',
  'kind_id'=>2, //直接使用kind_id做参数
  'tax_query'=>array(
    array(
      'taxonomy'=>'category',
      'field'=>'term_id',
      'operator'=>'IN',
      'terms'=>array(1)
    )
  )
);
query_posts($args);

好了,就到这里,这里在下只是提供了一种WordPress的查询优化方案,可用此方案自行改造其他应用。

谢谢观赏。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注