本文目录导读:

在PHP项目中使用聚合函数(如SUM()、COUNT()、AVG()、MAX()、MIN()等)时,需要注意以下几个关键点:
GROUP BY 子句的完整性
-- 错误:SELECT的字段没有全部包含在GROUP BY中 SELECT name, SUM(amount) FROM orders; -- SQL Mode ONLY_FULL_GROUP_BY下会报错 -- 正确:非聚合字段必须出现在GROUP BY中 SELECT name, SUM(amount) FROM orders GROUP BY name;
NULL值处理
// 聚合函数会忽略NULL值(COUNT(*)除外)
$result = $db->query("SELECT AVG(score) FROM students");
// 如果所有score都是NULL,返回NULL而非0
// COUNT(*) 会统计所有行,包括NULL
$result = $db->query("SELECT COUNT(*) FROM students");
// COUNT(column) 只统计非NULL的行
$result = $db->query("SELECT COUNT(score) FROM students");
性能优化
// 避免在GROUP BY中使用大字段
// 推荐使用索引列
$sql = "SELECT status, COUNT(*) FROM large_table GROUP BY status";
// status有索引
// 使用WHERE过滤后再聚合
$sql = "SELECT category, SUM(price)
FROM products
WHERE created_at > '2024-01-01'
GROUP BY category";
数据类型注意
// SUM() 和 AVG() 可能返回浮点数
$result = $db->query("SELECT SUM(price_cents) FROM orders");
// 返回 int 或 string,取决于驱动
// 使用 CAST 或 ROUND 控制精度
$result = $db->query("SELECT ROUND(AVG(price), 2) FROM products");
HAVING vs WHERE
// WHERE:在分组前过滤
// HAVING:在分组后过滤
$sql = "SELECT department, COUNT(*) as count
FROM employees
WHERE status = 'active' -- 先过滤
GROUP BY department
HAVING count > 10"; // 后过滤
内存和性能陷阱
// 避免在GROUP BY中使用大量唯一值
// 千万级用户表按省份分组没问题
// 但按用户ID分组会导致大量分组
// 大数据量时考虑限制
$sql = "SELECT category, SUM(views)
FROM page_views
GROUP BY category
ORDER BY SUM(views) DESC
LIMIT 10";
事务和锁
// 在事务中使用聚合函数要注意锁
$db->beginTransaction();
try {
// SELECT...FOR UPDATE 锁住相关行
$balance = $db->query("SELECT SUM(amount) FROM accounts WHERE user_id = 1 FOR UPDATE");
// 执行其他操作
$db->commit();
} catch (Exception $e) {
$db->rollback();
}
结果类型处理
// PDO中聚合函数结果可能是字符串
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$total = $result['total'];
// 需要手动转换类型
$total = (float) $result['total']; // 或 (int)
// 使用类型绑定
$stmt->bindColumn('total', $total, PDO::PARAM_INT);
子查询中的聚合
// 小心子查询中的聚合
$sql = "SELECT
department,
(SELECT AVG(salary) FROM employees WHERE department = e.department) as avg_salary
FROM employees e
GROUP BY department";
// 可能导致性能问题,考虑使用JOIN替代
错误处理
try {
$result = $db->query("SELECT SUM(non_existent_column) FROM table");
if ($result === false) {
// 处理错误
}
} catch (PDOException $e) {
// 记录错误日志
error_log("Aggregation query failed: " . $e->getMessage());
}
最佳实践示例
/**
* 安全使用聚合函数
*/
class AggregationService
{
private $db;
public function getCategoryStats(int $minCount = 5): array
{
$sql = "SELECT
category,
COUNT(*) as total_count,
COALESCE(SUM(price), 0) as total_price,
ROUND(AVG(rating), 1) as avg_rating,
MAX(created_at) as latest_item
FROM products
WHERE status = 'active'
GROUP BY category
HAVING total_count >= ?
ORDER BY total_price DESC
LIMIT 50";
$stmt = $this->db->prepare($sql);
$stmt->execute([$minCount]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 类型转换和安全处理
return array_map(function($row) {
return [
'category' => htmlspecialchars($row['category']),
'total_count' => (int)$row['total_count'],
'total_price' => (float)$row['total_price'],
'avg_rating' => (float)$row['avg_rating'],
'latest_item' => $row['latest_item']
];
}, $results);
}
}
- SQL模式:了解ONLY_FULL_GROUP_BY的影响
- 数据类型:注意自动类型转换
- 索引优化:GROUP BY字段要有索引
- NULL处理:理解不同聚合函数对NULL的处理
- 性能监控:大数据量时使用EXPLAIN分析
- 错误处理:检查执行结果和异常
- 数据验证:对结果进行类型转换和验证