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

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

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

  1. $args = array(
  2.   'post_type'=>'post',
  3.   'tax_query'=>array(
  4.     'relation'=>'AND',
  5.     array(
  6.       'taxonomy'=>'category',
  7.       'field'=>'term_id',
  8.       'operator'=>'IN',
  9.       'terms'=>array(1)
  10.     ),
  11.     array(
  12.       'taxonomy'=>'kind',
  13.       'field'=>'term_id',
  14.       'operator'=>'IN',
  15.       'terms'=>array(2),
  16.     )
  17.   )
  18. );
  19. query_posts($args);

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

  1. 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 (
  2.   wp_term_relationships.term_taxonomy_id IN (1)
  3.   AND
  4.   tt1.term_taxonomy_id IN (2)
  5. ) 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 类型就不别弄索引了)。

  1. //激活主题时执行
  2. function ashu_add_pages() {
  3.   global $pagenow;
  4.   if ( 'themes.php' == $pagenow && isset( $_GET['activated'] ) ){
  5.     ashuwp_alter_posts_table();
  6.   }
  7. }
  8. add_action( 'load-themes.php', 'ashu_add_pages' );
  9. 
    
  10. //修改posts表,增加一列kind_id
  11. function ashuwp_alter_posts_table(){
  12.   global $wpdb;
  13. 
    
  14.   //判断字段是否已经存在
  15.   $sql1 = "Describe {$wpdb->posts} `kind_id`";
  16.   $kind_id_exist = $wpdb->query($sql1);
  17. 
    
  18.   if( !$kind_id_exist ){
  19.     //新增列
  20.     $add_column = "ALTER TABLE {$wpdb->posts} ADD COLUMN `kind_id` INT(10) DEFAULT NULL";
  21.     $wpdb->query($add_column);
  22. 
    
  23.     //添加索引
  24.     $add_index = "ALTER TABLE {$wpdb->posts} ADD INDEX kind_id (`kind_id`)";
  25.     $wpdb->query($add_index);
  26. 
    
  27.   }
  28. 
    
  29. }

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

  1. add_action( 'set_object_terms', 'ashuwp_add_post_kind_id', 10, 6);
  2. function ashuwp_add_post_kind_id( $object_id, $terms, $tt_ids, $taxonomy, $append = false, $old_tt_ids = array() ){
  3. 
    
  4.   global $wpdb;
  5.   if( $taxonomy=='kind' ){
  6.     $term_id = reset( $tt_ids );
  7.     if( $term_id ){
  8.       $sql = "UPDATE {$wpdb->posts} SET `kind_id`={$term_id}  where `ID`={$object_id}";
  9.       $wpdb->get_results( $sql );
  10.     }
  11.   }
  12. 
    
  13. }

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

  1. function ashuwp_query_posts_where( $where, $query){
  2.   global $wpdb;
  3. 
    
  4.   $qv = $query->query_vars;
  5. 
    
  6.   isset( $qv['kind_id'] ) AND $kind_id = absint($qv['kind_id']) AND $where .= " AND {$wpdb->posts}.kind_id = {$kind_id}";
  7. 
    
  8.   return $where;
  9. 
    
  10. }
  11. add_filter( 'posts_where', 'ashuwp_query_posts_where', 10, 2);

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

  1. $args = array(
  2.   'post_type'=>'post',
  3.   'kind_id'=>2, //直接使用kind_id做参数
  4.   'tax_query'=>array(
  5.     array(
  6.       'taxonomy'=>'category',
  7.       'field'=>'term_id',
  8.       'operator'=>'IN',
  9.       'terms'=>array(1)
  10.     )
  11.   )
  12. );
  13. query_posts($args);

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

原创文章,作者:DavidWu,如若转载,请注明出处:https://www.davidwu.net/archives/80475

WordPress安全防护插件
服务项目 服务内容 收费标准(元)
主题/插件汉化 汉化团队WordPress主题/插件,翻译率95% (以标的主题/插件的句子数量为准)
服务器环境配置 基于您现有服务器,搭建配置网站运行环境,结合我们多年来实战经验,可完美支持WordPress等PHP程序运行,并配置伪静态规则、优化目录权限等问题。服务器我们强烈推荐使用Linux系统。 100元/次
网站托管 若贵站目前尚无技术人员,无法完成服务器环境配置,可选择我们的网站托管服务,直接交付正常运行的WordPress站点,并且无需担心服务器的后续维护工作,一切都由我们来帮您完成。 标配套餐:1000元/年/站点 高配套餐:联系客服获取
网站加速优化 从服务器后端配置优化到WordPress数据库缓存、前端页面缓存、JS和CSS压缩合并,全方位优化网站加载速度,实现秒开。(此服务仅针对(云)服务器/VPS) 500元/次(仅站内优化200元/次)
主题配置 本站所有主题均支持,可快速实现,若有任何问题可以咨询客服解决,若您希望我们提供配置服务,可选购此服务。 英文主题安装 60元/次 汉化主题安装 30元/次
HTTPS配置 HTTPS已经不断普及,并且有着更高的安全性以及SEO上的优待。该服务收取的为服务费,SSL证书产生的费用请自行承担。 100元/次
网站搬家 迁移网站所有文件和数据库信息、网站相关配置的调整、以及迁移中的疑难问题故障排除。 标准收费:500元/次 若网站数据量大,需协商
网站运维 提供整站的运维服务,保证网站正常运行。包含:网站故障定位及排除、网站数据备份和恢复、网站攻击及木马等问题的处理等 标准收费:2000元/年 IP 5000以上需协商

发表评论

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

51建站客服微信二维码
点击这里购买