
本文介绍如何通过单条 sql 查询一次性获取多个日期区间内各屏幕(screen_id)的独立预约数量,避免多次查询开销,并修正常见聚合逻辑错误。
本文介绍如何通过单条 sql 查询一次性获取多个日期区间内各屏幕(screen_id)的独立预约数量,避免多次查询开销,并修正常见聚合逻辑错误。
在实际业务中(如影院排片、广告屏投放统计),常需按不同周区间统计同一数据表(如 booking)中各资源(如 screen_id)的记录数。若为每个区间单独执行 SELECT COUNT(*) ... GROUP BY screen_id,将触发 6 次数据库往返,显著降低性能与可维护性。理想方案是用一条 SQL 返回所有区间计数,并确保结果按 screen_id 对齐、语义准确。
✅ 正确解法:条件聚合(推荐 · 高性能)
使用 CASE WHEN + SUM() 是最高效、最标准的方式。关键在于:将布尔表达式直接作为数值参与聚合(MySQL 中真为 1,假为 0),再用 SUM() 累加,而非 COUNT()。同时必须 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, SUM(CASE WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1 ELSE 0 END) AS fourthweekcount, SUM(CASE WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1 ELSE 0 END) AS fifthweekcount, SUM(CASE WHEN start_date <= ? AND DATE(end_date) >= ? THEN 1 ELSE 0 END) AS sixthweekcount FROM booking GROUP BY screen_id ORDER BY screen_id;
? 安全提示:示例中使用 ? 占位符,PHP 中务必通过 PDO 或 MySQLi 的预处理语句传入 $monday_week1, $sunday_week1 等变量,杜绝 SQL 注入。例如:
$stmt = $pdo->prepare($sql); $stmt->execute([ $monday_week1, $sunday_week1, $monday_week2, $sunday_week2, // ... 共 12 个参数 ]);
❌ 原写法问题解析
你尝试的 SUM((start_date)='$sunday_week1') 在语法上可行(MySQL 支持布尔转整型),但存在两个隐患:
立即学习“PHP免费学习笔记(深入)”;
用 php + mysql 驱动的在线商城系统,我们的目标为中国的中小企业及个人提供最简洁,最安全,最高效的在线商城解决方案,使用了自建的会员积分折扣功能,不同的会员组有不同的折扣,让您的商店吸引更多的后续客户。 系统自动加分处理功能,自动处理会员等级,免去人工处理的工作量,让您的商店运作起来更方便省事 采用了自建的直接模板技术,免去了模板解析时间,提高了代码利用效率 独立开发的购物车系统,使用最
- 未 GROUP BY screen_id:若遗漏该子句,整个表被当作一组聚合,返回单行结果,无法区分各屏幕;
- 逻辑覆盖重叠:若某条记录同时满足多个区间条件(如跨周预约),它会被重复计入多个 SUM,导致总数虚高——而这恰是业务所需(例如“该屏在第1周有几场”“在第2周有几场”,互不排斥)。
✅ 条件聚合天然支持这种“多标签计数”,且性能远优于子查询。
⚠️ 补充说明:子查询方式(仅作对比,不推荐)
虽可用派生表或 DUAL 实现(如答案中所示),但存在严重缺陷:
-- ❌ 错误示范(语法不兼容且语义错误) SELECT (SELECT COUNT(*) FROM booking WHERE ... GROUP BY screen_id), -- 子查询返回多行!报错 ... FROM DUAL;
MySQL 不允许子查询在 SELECT 列表中返回多行结果。若强行 GROUP BY screen_id,主查询无对应分组维度,结果不可控。因此子查询方案在此场景下不可行,原答案中的写法存在技术误导。
✅ 最终建议与总结
- 首选条件聚合:简洁、高效、可读性强,兼容所有主流 MySQL 版本(5.7+);
- 务必显式 GROUP BY screen_id:确保结果按屏幕维度对齐;
- 日期函数注意时区与索引:DATE(end_date) 会阻止索引使用,建议在 end_date 字段上建立函数索引(MySQL 8.0+)或改用 end_date >= ? AND end_date
- PHP 层处理:获取结果后,可直接遍历 $rows,每个元素即为一个 screen_id 对应的六维计数数组。
通过一条结构清晰的 SQL,你不仅能减少 83% 的数据库交互,还能获得更稳定、更易测试的统计逻辑。










