本文目录导读:

在 PHP 中实现指标聚合(类似数据库的 GROUP BY + SUM/COUNT/AVG),通常有以下几种常见场景和对应的实现方式:
数组聚合(纯 PHP 处理)
适用于从数据库取回数据后,需要在 PHP 内存中进一步聚合。
<?php
$data = [
['category' => 'A', 'amount' => 100],
['category' => 'A', 'amount' => 200],
['category' => 'B', 'amount' => 150],
['category' => 'B', 'amount' => 250],
['category' => 'C', 'amount' => 300],
];
// 按 category 聚合求和
$aggregated = [];
foreach ($data as $item) {
$cat = $item['category'];
if (!isset($aggregated[$cat])) {
$aggregated[$cat] = [
'category' => $cat,
'total' => 0,
'count' => 0,
];
}
$aggregated[$cat]['total'] += $item['amount'];
$aggregated[$cat]['count']++;
}
// 输出结果
print_r(array_values($aggregated));
SQL 聚合(推荐方式)
直接在数据库层面聚合,性能更好:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
// 原始 SQL 聚合
$stmt = $pdo->query("
SELECT
category,
SUM(amount) as total,
COUNT(*) as count,
AVG(amount) as avg_amount
FROM orders
GROUP BY category
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
多维数组聚合
当需要按多个字段分组时:
<?php
$data = [
['year' => 2023, 'month' => 1, 'sales' => 100],
['year' => 2023, 'month' => 1, 'sales' => 200],
['year' => 2023, 'month' => 2, 'sales' => 150],
['year' => 2024, 'month' => 1, 'sales' => 300],
];
// 多维聚合键
$aggregated = [];
foreach ($data as $item) {
$key = $item['year'] . '-' . $item['month'];
if (!isset($aggregated[$key])) {
$aggregated[$key] = [
'year' => $item['year'],
'month' => $item['month'],
'total_sales' => 0,
];
}
$aggregated[$key]['total_sales'] += $item['sales'];
}
使用 array_reduce 函数
函数式编程风格的聚合:
<?php
$data = [
['category' => 'A', 'amount' => 100],
['category' => 'A', 'amount' => 200],
['category' => 'B', 'amount' => 150],
];
$aggregated = array_reduce($data, function($carry, $item) {
$cat = $item['category'];
if (!isset($carry[$cat])) {
$carry[$cat] = ['category' => $cat, 'total' => 0, 'count' => 0];
}
$carry[$cat]['total'] += $item['amount'];
$carry[$cat]['count']++;
return $carry;
}, []);
print_r(array_values($aggregated));
使用集合类库(需要 Composer)
安装 illuminate/collections(Laravel 的集合类):
composer require illuminate/collections
<?php
require 'vendor/autoload.php';
use Illuminate\Support\Collection;
$data = new Collection([
['category' => 'A', 'amount' => 100],
['category' => 'A', 'amount' => 200],
['category' => 'B', 'amount' => 150],
]);
$aggregated = $data->groupBy('category')->map(function($items) {
return [
'category' => $items->first()['category'],
'total' => $items->sum('amount'),
'count' => $items->count(),
];
});
print_r($aggregated->values()->all());
时间序列聚合示例
对时间数据进行按小时、天、月聚合:
<?php
// 按天聚合
$aggregated_by_day = [];
foreach ($time_series_data as $item) {
$day = date('Y-m-d', strtotime($item['timestamp']));
if (!isset($aggregated_by_day[$day])) {
$aggregated_by_day[$day] = 0;
}
$aggregated_by_day[$day] += $item['value'];
}
性能建议
- 尽可能在数据库层面聚合 - SQL 的
GROUP BY比 PHP 内存循环快得多 - 使用索引 - 确保 GROUP BY 的字段有索引
- 分批处理大数据 - 对百万级数据,考虑分块处理
- 使用缓存 - 聚合结果如果变化不大,可以缓存
// 数据库聚合与缓存示例
function getSalesByCategory() {
$cacheKey = 'sales_by_category';
// 尝试从缓存获取
$cached = apcu_fetch($cacheKey);
if ($cached !== false) {
return $cached;
}
// 数据库聚合
$results = $db->query("SELECT category, SUM(amount) as total FROM orders GROUP BY category")->fetchAll();
// 缓存1小时
apcu_store($cacheKey, $results, 3600);
return $results;
}
选择哪种方式取决于你的具体场景:数据量大小、实时性要求、是否已使用框架等。