mysql多个左联接并按主表分组

mysql多个左联接并按主表分组,mysql,sql,left-join,Mysql,Sql,Left Join,我有下面的场景。一个地区有多个地区,一个地区有多个地址,一个地址在一个月内被访问多次。现在我想生成一个关于某个区域的月度报告。一个区域被访问了多少次。我已经编写了查询,但是结果集生成的区域较少,因为一些地址没有被访问。我有以下结构 桌子 区域:id |名称180行//名称唯一 地区:id |名称|区域| id 1k行 地址:id |名称|地区| id 80k行 访问地址:id |地址| id |日期|状态1M+行//状态=>1=已访问,2=待处理 我的问题如下 select ar.id as a

我有下面的场景。一个地区有多个地区,一个地区有多个地址,一个地址在一个月内被访问多次。现在我想生成一个关于某个区域的月度报告。一个区域被访问了多少次。我已经编写了查询,但是结果集生成的区域较少,因为一些地址没有被访问。我有以下结构

桌子

区域:id |名称180行//名称唯一

地区:id |名称|区域| id 1k行

地址:id |名称|地区| id 80k行

访问地址:id |地址| id |日期|状态1M+行//状态=>1=已访问,2=待处理

我的问题如下

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
where     month(va.date) = '01'
and       year(va.date)='2020'
group by  ar.id

面积表包含180个面积,但结果集仅显示144个面积。我的错误在哪里?对此有何解释?缺少这些区域,因为它们没有访问权限。

尝试将WHERE子句中的逻辑移动到相应联接的ON子句:

SELECT
    ar.id AS area_id,
    ar.name AS area,
    COUNT(CASE WHEN va.status = 1 THEN 1 END) AS visited,
    COUNT(CASE WHEN va.status = 2 THEN 1 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
    va.date >= '2020-01-01' AND va.date <  '2020-02-01'
GROUP BY
    ar.id;

这可能有助于加快对此表的联接。

WHERE子句正在将具有访问地址的左联接转换为内部联接。因为它是左联接链中最右边的表,所以所有联接都将转换为内部联接。为防止出现这种情况,应将相应的条件从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
并在应用程序代码中将“已访问”、“待处理”和“总计”设置为零时,将缺少的区域添加到第一个结果中

内部联接应该快得多,因为现在引擎可以开始使用WHERE条件的索引从访问_地址中只读取必要的行

您还可以使用更复杂但单一的查询。其思想是将左连接与预聚合子查询一起使用:

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

我已复制粘贴了您的查询。现在它的速度非常慢:。。所有表中的日期字段和外键都编制了索引。获取超时错误。有没有更好的方法来写这个查询?NoOB考虑用文字重写日期限制,也添加索引。谢谢你的努力。最后一个问题是我实际上在寻找的答案。我试着从mysql端而不是PHP端使用一个查询来完成它。非常感谢。。
select ar.id as area_id, ar.name as area 
from areas ar
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