You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何快速准确获取Woocommerce各层级自定义分类销售总额?

WooCommerce自定义分类销售总额导出性能优化

问题背景

站点使用自定义产品分类my_taxonomy,层级结构为Level0→Level1→Level2,需要统计Level1和Level2分类的销售总额并导出CSV。原代码在订单量、分类数量较多时,因多层循环嵌套、重复数据库查询导致执行超时或站点崩溃。

核心优化方案

1. 用SQL直接统计总额,替代多层循环遍历

原代码通过wc_get_orders获取所有订单后,嵌套循环订单、商品、分类进行判断累加,时间复杂度极高。改为直接通过SQL关联订单、订单项、产品分类表,一次性计算出各分类的销售总额,大幅减少数据库查询次数和内存占用。

2. 批量获取分类并构建层级映射

原代码嵌套调用get_terms三次,每次都会发起数据库请求。改为一次性获取所有my_taxonomy分类,然后通过父ID构建Level1和Level2的映射关系,减少数据库查询次数。

3. 直接输出CSV,跳过服务器中间文件

原代码先将CSV写入服务器文件再读取输出,增加了磁盘IO开销。改为直接通过php://output输出CSV内容,省去文件读写步骤。

4. 优化旧文件清理逻辑

原代码每次导出都删除所有旧文件,改为只删除7天前的旧文件,避免不必要的磁盘操作。

优化后的完整代码

function handle_csv_export() {
    if (!wp_verify_nonce($_POST['_wpnonce'], 'generate-csv')) {
        wp_die('非法请求');
    }

    global $wpdb;

    // 一次性获取所有my_taxonomy分类,构建层级映射
    $all_terms = get_terms(array(
        'taxonomy' => 'my_taxonomy',
        'hide_empty' => false,
        'fields' => 'all'
    ));

    $level1_map = []; // key: term_id, value: term_name
    $level2_map = []; // key: term_id, value: term_name

    foreach ($all_terms as $term) {
        // 筛选Level0下的Level1(父ID为Level0分类ID)
        $parent_term = get_term($term->parent, 'my_taxonomy');
        if ($parent_term && $parent_term->parent === 0) {
            $level1_map[$term->term_id] = $term->name;
        }
        // 筛选Level1下的Level2(父ID为Level1分类ID)
        elseif (isset($level1_map[$term->parent])) {
            $level2_map[$term->term_id] = $term->name;
        }
    }

    // SQL查询Level1分类的销售总额
    $level1_term_ids = !empty($level1_map) ? implode(',', array_keys($level1_map)) : '0';
    $level1_sql = "
        SELECT t.name AS term_name, SUM(oim.meta_value) AS total_amount
        FROM {$wpdb->prefix}woocommerce_order_items oi
        JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
        JOIN {$wpdb->prefix}posts p ON oi.order_id = p.ID
        JOIN {$wpdb->prefix}term_relationships tr ON oi.product_id = tr.object_id
        JOIN {$wpdb->prefix}term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
        JOIN {$wpdb->prefix}terms t ON tt.term_id = t.term_id
        WHERE p.post_status IN ('wc-completed', 'wc-processing', 'wc-on-hold')
          AND oim.meta_key = '_line_total'
          AND tt.taxonomy = 'my_taxonomy'
          AND tt.term_id IN ($level1_term_ids)
        GROUP BY t.term_id
    ";
    $level1_results = $wpdb->get_results($level1_sql, ARRAY_A);

    // SQL查询Level2分类的销售总额
    $level2_term_ids = !empty($level2_map) ? implode(',', array_keys($level2_map)) : '0';
    $level2_sql = "
        SELECT t.name AS term_name, SUM(oim.meta_value) AS total_amount
        FROM {$wpdb->prefix}woocommerce_order_items oi
        JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
        JOIN {$wpdb->prefix}posts p ON oi.order_id = p.ID
        JOIN {$wpdb->prefix}term_relationships tr ON oi.product_id = tr.object_id
        JOIN {$wpdb->prefix}term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
        JOIN {$wpdb->prefix}terms t ON tt.term_id = t.term_id
        WHERE p.post_status IN ('wc-completed', 'wc-processing', 'wc-on-hold')
          AND oim.meta_key = '_line_total'
          AND tt.taxonomy = 'my_taxonomy'
          AND tt.term_id IN ($level2_term_ids)
        GROUP BY t.term_id
    ";
    $level2_results = $wpdb->get_results($level2_sql, ARRAY_A);

    // 准备CSV内容并直接输出到浏览器
    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename="taxonomy-sales-export.csv"');
    $output = fopen('php://output', 'w');

    // 写入Level1数据
    fputcsv($output, ['Level1分类', '销售总额']);
    foreach ($level1_results as $row) {
        fputcsv($output, [$row['term_name'], number_format($row['total_amount'], 2) . '$']);
    }

    // 空行分隔
    fputcsv($output, []);

    // 写入Level2数据
    fputcsv($output, ['Level2分类', '销售总额']);
    foreach ($level2_results as $row) {
        fputcsv($output, [$row['term_name'], number_format($row['total_amount'], 2) . '$']);
    }

    fclose($output);

    // 清理7天前的旧导出文件
    $upload_dir = wp_upload_dir();
    $folder_path = $upload_dir['basedir'] . '/myorders';
    if (file_exists($folder_path)) {
        $expire_time = time() - (7 * 24 * 60 * 60);
        $files = glob($folder_path . '/*.csv');
        foreach ($files as $file) {
            if (filemtime($file) < $expire_time) {
                unlink($file);
            }
        }
    }

    exit;
}

优化效果说明

  • 时间复杂度大幅降低:原代码需要遍历所有订单×所有商品×所有分类,优化后仅通过2次SQL查询完成统计,性能提升显著。
  • 减少数据库查询次数:原代码多次调用get_terms和has_term,优化后仅1次分类查询+2次统计查询。
  • 降低内存占用:无需加载所有订单和商品数据到内存,直接通过数据库计算结果。
  • 减少磁盘IO:直接输出CSV,避免中间文件的读写操作。

内容的提问来源于stack exchange,提问作者Dias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 07:02:08