
本文介绍如何通过单条 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;
? 关键说明:
⚠️ 备选方案:子查询嵌套(语法可行,但慎用)
部分旧版 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 条件聚合。它语义清晰、性能卓越、易于维护,是处理“单表多条件独立计数”问题的标准解法。