如何解决MySQL中‘Subquery returns more than 1 row’错误(CodeIgniter场景)
问题原因分析
这个错误很常见,咱们先拆解下问题:你写的每个子查询里都加了GROUP BY tanggal,这会让子查询返回所有日期的统计结果(多行数据),但外层查询期望每个主查询行(对应单个日期)只能拿到子查询的一个单一值,数据库自然就报错了。
举个直观的例子:子查询SELECT COUNT(*) from absensi_siswa where kehadiran="alfa" GROUP BY tanggal会返回每个日期的alfa人数,是一个多行结果集,但外层查询需要的是当前行日期对应的alfa人数,所以子查询必须返回单个值才行。
解决方案1:修正子查询,添加关联条件
给每个子查询加上WHERE条件关联外层的日期,同时去掉子查询里的GROUP BY(因为已经过滤到单个日期了):
$query = $this->db->query(' SELECT tanggal, (SELECT COUNT(*) FROM absensi_siswa AS sub WHERE sub.kehadiran="alfa" AND sub.tanggal = main.tanggal) AS alfa, (SELECT COUNT(*) FROM absensi_siswa AS sub WHERE sub.kehadiran="izin" AND sub.tanggal = main.tanggal) AS izin, (SELECT COUNT(*) FROM absensi_siswa AS sub WHERE sub.kehadiran="hadir" AND sub.tanggal = main.tanggal) AS hadir, (SELECT COUNT(*) FROM absensi_siswa AS sub WHERE sub.kehadiran="sakit" AND sub.tanggal = main.tanggal) AS sakit FROM absensi_siswa AS main GROUP BY tanggal '); return $query->result();
这里给主表起了别名main,子表起了别名sub,通过sub.tanggal = main.tanggal让子查询只统计当前主行日期的对应状态人数,每个子查询都会返回单个值,就不会触发报错了。
解决方案2:使用条件聚合(更高效,推荐)
上面的方法需要多次扫描表(每个子查询扫一次),数据量大的时候性能会打折扣。更优的方式是用条件聚合,只扫描一次表就能完成所有统计:
$query = $this->db->query(' SELECT tanggal, SUM(CASE WHEN kehadiran = "alfa" THEN 1 ELSE 0 END) AS alfa, SUM(CASE WHEN kehadiran = "izin" THEN 1 ELSE 0 END) AS izin, SUM(CASE WHEN kehadiran = "hadir" THEN 1 ELSE 0 END) AS hadir, SUM(CASE WHEN kehadiran = "sakit" THEN 1 ELSE 0 END) AS sakit FROM absensi_siswa GROUP BY tanggal '); return $query->result();
原理很简单:CASE WHEN会逐行判断当前记录的状态,符合就返回1,否则返回0,再用SUM把同一日期的数值累加,就是该状态的总人数。这种方法只需要遍历一次表,性能比子查询好很多。
内容的提问来源于stack exchange,提问作者Cinta Dewi Amelia
相关产品推荐
相关产品推荐

