php - WordPress 选择计数查询优化

标签 php mysql wordpress performance woocommerce

我们有一个相当大的 WordPress mysql 数据库(9.8 GB)

关键表中的行:

  • 77 万个 wp_posts
  • 14K wp_terms
  • 4M wp_term_relationships
  • 14K wp_term_taxonomy
  • 16M wp_postmeta
  • 1M wp_options(该死的 transient )

我有一个想要优化的查询。我已经在这个问题上挣扎了一段时间了。它在基于 woocommerce 的网站上被大量使用。我想知道是否有人可以优化这个查询。

SELECT  COUNT( DISTINCT ID )
    FROM  wp_posts p
    LEFT JOIN  
        ( SELECT  object_id
            FROM  wp_term_relationships
            WHERE  term_taxonomy_id IN ( 8128 ) 
        ) AS exclude_join  ON exclude_join.object_id = p.ID
    INNER JOIN  
        ( SELECT  object_id
            FROM  wp_term_relationships
            INNER JOIN  wp_term_taxonomy using( term_taxonomy_id )
            WHERE  term_id IN ( 20,21,31,46,3591,47,99 ) 
        ) AS include_join  ON include_join.object_id = p.ID
    WHERE  1=1
      AND  p.post_status = 'publish'
      AND  p.post_type = 'product'
      AND  exclude_join.object_id IS NULL

这是重新计算产品的查询,可在 wc-terms-functions.php 中找到。

function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
global $wpdb;

// Standard callback.
if ( $callback ) {
    _update_post_term_count( $terms, $taxonomy );
}

$exclude_term_ids            = array();
$product_visibility_term_ids = wc_get_product_visibility_term_ids();

if ( $product_visibility_term_ids['exclude-from-catalog'] ) {
    $exclude_term_ids[] = $product_visibility_term_ids['exclude-from-catalog'];
}

if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids['outofstock'] ) {
    $exclude_term_ids[] = $product_visibility_term_ids['outofstock'];
}

$query = array(
    'fields' => "
        SELECT COUNT( DISTINCT ID ) FROM {$wpdb->posts} p
    ",
    'join'   => '',
    'where'  => "
        WHERE 1=1
        AND p.post_status = 'publish'
        AND p.post_type = 'product'

    ",
);

if ( count( $exclude_term_ids ) ) {
    $query['join']  .= " LEFT JOIN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( " . implode( ',', array_map( 'absint', $exclude_term_ids ) ) . " ) ) AS exclude_join ON exclude_join.object_id = p.ID";
    $query['where'] .= " AND exclude_join.object_id IS NULL";
}

// Pre-process term taxonomy ids.
if ( ! $terms_are_term_taxonomy_ids ) {
    // We passed in an array of TERMS in format id=>parent.
    $terms = array_filter( (array) array_keys( $terms ) );
} else {
    // If we have term taxonomy IDs we need to get the term ID.
    $term_taxonomy_ids = $terms;
    $terms             = array();
    foreach ( $term_taxonomy_ids as $term_taxonomy_id ) {
        $term    = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
        $terms[] = $term->term_id;
    }
}

// Exit if we have no terms to count.
if ( empty( $terms ) ) {
    return;
}

// Ancestors need counting.
if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
    foreach ( $terms as $term_id ) {
        $terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) );
    }
}

// Unique terms only.
$terms = array_unique( $terms );

// Count the terms.
foreach ( $terms as $term_id ) {
    $terms_to_count = array( absint( $term_id ) );

    if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
        // We need to get the $term's hierarchy so we can count its children too
        if ( ( $children = get_term_children( $term_id, $taxonomy->name ) ) && ! is_wp_error( $children ) ) {
            $terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
        }
    }

    // Generate term query
    $term_query          = $query;
    $term_query['join'] .= " INNER JOIN ( SELECT object_id FROM {$wpdb->term_relationships} INNER JOIN {$wpdb->term_taxonomy} using( term_taxonomy_id ) WHERE term_id IN ( " . implode( ',', array_map( 'absint', $terms_to_count ) ) . " ) ) AS include_join ON include_join.object_id = p.ID";

    // Get the count
    $count = $wpdb->get_var( implode( ' ', $term_query ) );

    // Update the count
    update_woocommerce_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
}

delete_transient( 'wc_term_counts' );

SELECT SQL_CALC_FOUND_ROWS 是否是更好的选择?我不是查询专家,请帮忙。

最佳答案

首先改进多:多模式,如下所述:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

SQL_CALC_FOUND_ROWS 主要是为了方便,这样您就不必扫描表两次。 (目前尚不清楚您如何在这里使用它。)

该查询看起来像“爆炸-内爆”情况,其中执行JOIN会导致更多行,然后GROUP BY(或DISTINCT)你的情况)缩小了。

我不清楚为什么在一种情况下使用 LEFT JOIN,而在另一种情况下使用 JOIN

可能每个 JOIN 都可以按以下格式添加到 WHERE 子句中:

AND EXISTS (SELECT ... ) 

这会让你摆脱DISTINCT,从而避免爆炸内爆。此时,一个简单的 COUNT(*) 就足够了。

关于php - WordPress 选择计数查询优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47467331/

相关文章:

PHP preg_match_all 失败

mysql - 为每位患者在日期之前选择一个预约 (MySQL)

wordpress - 古腾堡 : Allow additional formatting in RichText

php - 无法在 WordPress ACF 重复器字段上使用 have_rows() 的字段名称

php - mysql 查询中奇怪的额外等号。我该如何删除它?

php - 比较两个表如果重复列则更新,如果不匹配则插入

mysql - 如何设计数据库表来跟踪价格变化

具有多个变量组件的 PHP MySQL 语句

php - 如何借助wordpress中的wp-config文件获取mysql数据库数据

php - 如何在 codeigniter 中下载文件?