有没有办法提高这个查询的速度?

时间:2016-03-09 作者:Andrew Lazarus

我需要交叉引用两种分类法,product_categoriesbrands. 我能想到的唯一方法是获取一个分类法中的所有帖子,然后获取另一个分类法中存在的所有帖子:

<?php

  $category = /* Taxonomy object selected from list */;

  // Get all the posts in this query
  $args = array(
    \'post_type\' => \'product\',
    \'tax_query\' => array(
      array(
        \'taxonomy\' => $category->taxonomy,
        \'field\'    => \'slug\',
        \'terms\'    => $category->slug
      )
    )
  );
  $product_items = get_posts( $args );

  // create a blank array ready for the IDs
  $product_items_ids = [];

  // For every post found, populate the array
  foreach ($product_items as $product_item) {
    // Create array from all post IDs in category
    $product_items_ids[] = $product_item->ID;
  }

  // Get all terms in Brand taxonomy that are in the array above
  $brand_items = wp_get_object_terms( $product_items_ids, \'brand\' );

  // Output the information needed
  foreach ($brand_items as $brand_item) { ?>
    <li><a href="<?php echo get_term_link( $brand_item->slug,  $brand_item->taxonomy ); ?>"> <?php echo $brand_item->name; ?></a></li>
  <?php } ?>
我会打5-10次电话,如果产品列表变得庞大,这意味着我会在网站上的每个帖子上打10次电话来加载菜单等。

有没有更有效的方法来执行这种类型的查询?

添加需要注意的事项:

第一个查询将引入分配了$category->taxonomy.

$category 是由高级自定义字段在管理中选择的分类对象。对于这个示例,假设accessories 是一个术语,并且product_cat 是分类法@PieterGoosen

我需要返回品牌中的所有条款,这些条款也是带有附件条款的帖子。

2 个回复
最合适的回答,由SO网友:Pieter Goosen 整理而成

我们可以做很多事情来提高代码的性能。让我们先设置一些基准

基准点我正在用

  • category 分类术语,有9个职位和

    post_tag 具有61个匹配标记的分类法。

    根据您当前的代码,我得到以下结果

    • 69 queries in =/- 0.4s
    对于这样一个小的数据库和测试主题,这是非常昂贵和大量的查询

    优化我们要做的第一件事是只从帖子中查询帖子ID,原因如下

    我们不需要任何post数据

    我们不需要更新后缓存和后元缓存,我们不需要

    显然,只有查询帖子ID才能大幅提高性能

仅查询ID有一个缺点,即我们还失去了术语缓存。因为我们不更新术语缓存,这将导致db查询的大量增加。为了解决这个问题,我们将使用update_object_term_cache.

到目前为止,仅在您的查询中,您就获得了1db的调用和0.02s,这并不多,但它对一个巨大的数据库有很大的影响。真正的收益将在下一节中介绍

真正大的好处是将object一词传递给get_term_link(), 而不是术语ID。如果术语缓存中没有术语,则将术语ID传递给get_term_link(), 而不是从缓存中获取术语对象,get_term_link() 将查询db以获取术语对象。仅在测试中,这相当于额外的61 db调用,每个标记一个。想想几百个标签。

我们已经有了术语对象,所以我们可以简单地传递完整的术语对象。你应该一直这样做。即使术语对象在缓存中,传递术语ID的速度仍然非常慢,因为我们仍然必须从缓存中获取术语对象

我已经清理了你的代码。注意,我使用了短数组语法,它确实需要PHP 5.4+。下面是您的代码的外观

$category       = get_category( 13 ); // JUST FOR TESTING< ADJUST TO YOUR NEEDS

$args = [
    \'post_type\' => \'product\',
    \'fields\'    => \'ids\', // Only query the post ID\'s, not complete post objects
    \'tax_query\' => [
        [
            \'taxonomy\'  => $category->taxonomy,
            \'field\'     => \'slug\',
            \'terms\'     => $category->slug
        ]
    ]
];
$ids = get_posts( $args );

$links = [];
// Make sure we have ID\'saves
if ( $ids ) {
    /**
     * Because we only query post ID\'s, the post caches are not updated which is
     * good and bad
     *
     * GOOD -> It saves on resources because we do not need post data or post meta data
     * BAD -> We loose the vital term cache, which will result in even more db calls
     *
     * To solve that, we manually update the term cache with update_object_term_cache
     */
    update_object_term_cache( $ids, \'product\' );

    $term_names = [];

    foreach ( $ids as $id ) {
        $terms = get_object_term_cache( $id, \'post_tag\' );
        foreach ( $terms as $term ) {
            if ( in_array( $term->name, $term_names ) )
                continue;

            $term_names[] = $term->name;

            $links[$term->name] = \'<li><a href="\' . get_term_link( $term ) . \'">\' . $term->name . \'</a></li>\';
        }
    }
}

if ( $links ) {
    ksort( $links );
    $link_string = implode( "\\n\\t" , $links );
} else {
    $link_string = \'\';
}

echo $link_string;
目前,我们已将人数减少到6 db queries in 0.04s 这是一个巨大的进步。

我们甚至可以更进一步,将结果存储在瞬态中

$category       = get_category( 13 ); // JUST FOR TESTING< ADJUST TO YOUR NEEDS

$link_string    = \'\';
$transient_name = \'query_\' . md5( $category->slug . $category->taxonomy );
if ( false === ( $link_string = get_transient( $transient_name ) ) ) {
    $args = [
        \'post_type\' => \'product\',
        \'fields\'    => \'ids\', // Only query the post ID\'s, not complete post objects
        \'tax_query\' => [
            [
                \'taxonomy\'  => $category->taxonomy,
                \'field\'     => \'slug\',
                \'terms\'     => $category->slug
            ]
        ]
    ];
    $ids = get_posts( $args );

    $links = [];
    // Make sure we have ID\'saves
    if ( $ids ) {
        /**
         * Because we only query post ID\'s, the post caches are not updated which is
         * good and bad
         *
         * GOOD -> It saves on resources because we do not need post data or post meta data
         * BAD -> We loose the vital term cache, which will result in even more db calls
         *
         * To solve that, we manually update the term cache with update_object_term_cache
         */
        update_object_term_cache( $ids, \'product\' );

        $term_names = [];

        foreach ( $ids as $id ) {
            $terms = get_object_term_cache( $id, \'post_tag\' );
            foreach ( $terms as $term ) {
                if ( in_array( $term->name, $term_names ) )
                    continue;

                $term_names[] = $term->name;

                $links[$term->name] = \'<li><a href="\' . get_term_link( $term ) . \'">\' . $term->name . \'</a></li>\';
            }
        }
    }


    if ( $links ) {
        ksort( $links );
        $link_string = implode( "\\n\\t" , $links );
    } else {
        $link_string = \'\';
    }

    set_transient( $transient_name, $link_string, 7 * DAY_IN_SECONDS );
}   

echo $link_string;
这将使一切减少到2 queries in 0.002s. 有了临时版本,我们将在发布、更新、删除或取消删除帖子时刷新临时版本。我们将使用transition_post_status 钩住这里

add_action( \'transition_post_status\', function ()
{
    global $wpdb;
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient%_query_%\')" );
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient_timeout%_query_%\')" );
});

SO网友:Sumit

我不太擅长SQL。但是这个查询对我来说很有用,我只是将两个查询合并为一个!

$brands = $wpdb->get_results("SELECT DISTINCT term_id "
        . "FROM {$wpdb->prefix}term_taxonomy "
        . "LEFT JOIN {$wpdb->prefix}term_relationships my_tr ON {$wpdb->prefix}term_taxonomy.term_taxonomy_id = my_tr.term_taxonomy_id "
        . "WHERE object_id IN (SELECT object_id FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = {$category->term_taxonomy_id}) "
        . "AND taxonomy = \'your_brand_taxonomy\'");
但是,我建议您使用父项和子项来更改数据结构,而不是使用两种不同的分类法。

相关推荐

将wp_Query替换为wp_User_Query

我在插件中制作了一些订阅者档案和单曲(个人资料页),其中我还包括了一个“用户单曲”模板,通过template_include. 不过,我正在尝试从插件中删除一些模板,以使其使用主题模板。我用了locate_template( \'single.php\' ) 从活动主题中选择单个模板。我没有使用全局wp_query 在本例中显示我的内容,但页面显示了基于查询默认值的循环(十篇帖子)。我想知道的是,我是否可以完全放弃默认查询,用wp_user_query 我可以将查询到的用户ID输入其中。然后我想筛选the