您的 WHERE 子句正在将带有 visiting_addresses 的 LEFT JOIN 转换为 INNER JOIN。由于它是 LEFT-JOIN 链中最右边的表,所有连接都将转换为 INNER JOINS。为了防止这种情况,您应该将相应的条件从 WHERE 子句移到 ON 子句:
select ar.id as area_id, ar.name as area,
sum(case when va.status = 1 then 1 else 0 end) as visited,
sum(case when va.status = 2 then 1 else 0 end) as pending,
count(va.id) as total
from areas ar
left join territories t on t.area_id=ar.id
left join addresses a on a.territory_id=t.id
left join visiting_addresses va
on va.address_id=a.id
and month(va.date) = '01'
and year(va.date)='2020'
group by ar.id
但由于您有很多行,我宁愿运行两个查询。首先使用内部连接仅获取上个月具有地址的区域。您应该更改va.date 上的条件以使用索引:
select ar.id as area_id, ar.name as area,
sum(case when va.status = 1 then 1 else 0 end) as visited,
sum(case when va.status = 2 then 1 else 0 end) as pending,
count(va.id) as total
from areas ar
join territories t on t.area_id=ar.id
join addresses a on a.territory_id=t.id
join visiting_addresses va on va.address_id=a.id
where va.date >= '2020-01-01'
and va.date < '2020-02-01'
group by ar.id
确保您在visiting_addresses(date) 上有索引,或者在visiting_addresses(date, address_id, status) 上有更好的索引。
然后用一个简单的方法得到所有区域
select ar.id as area_id, ar.name as area
from areas ar
并在将visited、pending 和total 设置为零(在应用程序代码中)时将缺失区域添加到第一个结果中。
INNER JOIN 应该快得多,因为现在引擎可以使用 WHERE 条件的索引从 visiting_addresses 开始仅读取必要的行。
您还可以使用更复杂但单一的查询。想法是使用带有预聚合子查询的 LEFT JOIN:
select ar.id as area_id, ar.name as area,
coalesce(visited, 0) as visited,
coalesce(pending, 0) as pending,
coalesce(total, 0) as total
from areas ar
left join (
select t.area_id
sum(case when va.status = 1 then 1 else 0 end) as visited,
sum(case when va.status = 2 then 1 else 0 end) as pending,
count(va.id) as total
from territories t
join addresses a on a.territory_id=t.id
join visiting_addresses va on va.address_id=a.id
where va.date >= '2020-01-01'
and va.date < '2020-02-01'
group by t.area_id
) x on x.area_id = ar.id