
本文讲解如何在单条 sql 查询中,将两个表按日期关联后,按工人(worker)和类型(type)双重分组统计数量,避免 php 多次查询,提升效率与可维护性。
本文讲解如何在单条 sql 查询中,将两个表按日期关联后,按工人(worker)和类型(type)双重分组统计数量,避免 php 多次查询,提升效率与可维护性。
在实际业务中(如工单统计、排班计费、任务分配等),我们常需将操作记录表(如 table1,含日期和任务类型)与人员排班表(如 table2,含日期与对应工人 ID)进行关联,并按「工人 + 类型」维度聚合统计——例如计算每位工人每天完成的各类任务数量,为后续按类型计费提供数据基础。
原始方案存在明显瓶颈:先用 GROUP BY date, type 统计 table1,再在 PHP 中循环对每个日期单独查 table2 获取 worker,不仅产生 N+1 查询问题,还导致逻辑分散、难以 SQL 层面统一聚合。理想解法是在数据库层完成关联 + 分组 + 聚合,一条语句输出最终结果。
✅ 正确写法如下(使用 LEFT JOIN + 多字段 GROUP BY):
SELECT t1.date, t1.type, t2.worker, COUNT(*) AS qnt FROM table1 AS t1 LEFT JOIN table2 AS t2 ON t1.date = t2.date GROUP BY t1.date, t2.worker, t1.type;
执行结果示例:
date | type | worker | qnt ----------|------|--------|---- 22/05/23 | 1 | 20 | 2 22/05/23 | 2 | 20 | 1 22/05/24 | 1 | 23 | 1 22/05/25 | 2 | 17 | 1
? 关键要点说明:
-
LEFT JOIN确保不丢失table1中的任何记录:即使某日期在table2中无对应工人(如排班遗漏),该行仍保留,worker字段为NULL,便于排查数据完整性; -
GROUP BY必须包含所有非聚合字段:t1.date、t2.worker、t1.type缺一不可,否则 MySQL 8.0+ 会报错(严格模式下); - 若需进一步按
worker和type汇总(忽略日期),可简化GROUP BY t2.worker, t1.type,并移除t1.date的SELECT; - 注意日期格式一致性:确保两表
date字段类型均为DATE或统一字符串格式(建议使用DATE类型并标准化存储,避免'22/05/23'这类易出错的字符串比较)。
⚠️ 常见误区提醒:
- ❌ 错误尝试子查询嵌套聚合(如
SUM(SELECT ...)):SQL 不允许在SELECT列表中直接嵌套未关联的聚合子查询; - ❌ 使用
INNER JOIN替代LEFT JOIN:将导致table1中无排班记录的日期被静默过滤,造成统计遗漏; - ❌ 忘记在
GROUP BY中包含t2.worker:会导致同一日期多个工人被错误合并(尤其当table2存在重复日期时)。
该方案不仅满足当前需求,还具备良好扩展性——后续如需加入单价表(type → unit_price),只需再 JOIN 并计算 qnt * unit_price 即可直接得出应付金额,真正实现“一次查询、端到端聚合”。


















