
本文介绍如何通过单条 sql 语句对同一数据表按多个时间区间分别统计记录数,并兼容 php 使用场景,重点对比子查询法与 case 聚合法的适用性、性能差异及常见陷阱。
本文介绍如何通过单条 sql 语句对同一数据表按多个时间区间分别统计记录数,并兼容 php 使用场景,重点对比子查询法与 case 聚合法的适用性、性能差异及常见陷阱。
在开发报表或数据看板时,常需对同一张表(如 booking)按不同时间窗口(例如连续六周)分别统计满足条件的记录数量,且要求每个计数结果作为独立列返回——而非合并汇总。直接执行六条独立 SELECT COUNT(*) 查询虽逻辑清晰,但会引发多次数据库往返,显著降低 PHP 应用性能。理想方案是单次查询、多维计数。
✅ 推荐方案:使用 CASE WHEN + SUM() 进行条件聚合(高性能)
这是最高效、最符合 SQL 标准的写法,避免重复扫描表,且天然支持 GROUP BY screen_id:
SELECT
screen_id,
screen,
screen_code,
SUM(CASE
WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1
ELSE 0
END) AS firstweekcount,
SUM(CASE
WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1
ELSE 0
END) AS secondweekcount,
SUM(CASE
WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1
ELSE 0
END) AS thirdweekcount,
-- ... 同理补充 fourthweekcount 至 sixthweekcount
SUM(CASE
WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1
ELSE 0
END) AS sixthweekcount
FROM booking
GROUP BY screen_id, screen, screen_code;? 关键说明:
- 使用 ? 占位符(PDO 预处理)替代 PHP 变量拼接(如 $monday_week1),彻底防止 SQL 注入;
- DATE(end_date) 显式转换确保索引可用(若 end_date 为 DATETIME 类型);
- SUM(... THEN 1 ELSE 0) 比 COUNT(...) 更安全:即使条件全不满足,仍返回 0 而非 NULL;
- 所有 GROUP BY 字段必须显式列出(MySQL 5.7+ 严格模式要求)。
⚠️ 备选方案:子查询嵌套(语法可行,但慎用)
部分旧版 MySQL 或特殊场景下可使用如下结构(需 DUAL 表支持):
立即学习“PHP免费学习笔记(深入)”;
SELECT (SELECT COUNT(*) FROM booking WHERE start_date <= ? AND DATE(end_date) >= ?) AS firstweekcount, (SELECT COUNT(*) FROM booking WHERE start_date <= ? AND DATE(end_date) >= ?) AS secondweekcount, -- ... 其余五列 (SELECT COUNT(*) FROM booking WHERE start_date <= ? AND DATE(end_date) >= ?) AS sixthweekcount;
❗ 严重限制:
- 此写法无法按 screen_id 分组——它只返回一行总计数,丢失屏幕维度;
- 每个子查询都会全表扫描一次,6 个子查询 = 6 次全表扫描,性能随数据量指数级恶化;
- 若需分组结果,必须改用 JOIN 或窗口函数(MySQL 8.0+),复杂度陡增。
? PHP 实现要点(以 PDO 为例)
$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare($sql); // 上述 CASE 聚合 SQL
// 绑定全部 12 个参数(6 周 × 每周 2 个日期)
$params = [
$monday_week1, $sunday_week1,
$monday_week2, $sunday_week2,
// ... 直至 $monday_week6, $sunday_week6
];
$stmt->execute($params);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $row) {
echo "Screen {$row['screen_id']}: Week1={$row['firstweekcount']}, Week2={$row['secondweekcount']}\n";
}✅ 总结建议
| 方案 | 是否支持分组 | 性能 | 安全性 | 推荐度 |
|---|---|---|---|---|
| CASE WHEN + SUM() | ✅ 完美支持 | ⭐⭐⭐⭐⭐(单次扫描) | ✅ 预处理防注入 | ★★★★★ 强烈推荐 |
| 多子查询(无 GROUP BY) | ❌ 仅全局计数 | ⭐(N 次全表扫描) | ⚠️ 易拼接漏洞 | ★☆☆☆☆ 不推荐用于分组场景 |
最终结论:始终优先采用 CASE WHEN 条件聚合。它语义清晰、性能卓越、易于维护,是处理“单表多条件独立计数”问题的标准解法。











