有个需求是这样的:大区和省份是一对多的关系。那么肯定在省份表中,有个逻辑外键叫大区id。这时候,我想看看大区的名称、属性、负责人等信息,还想看看大区下的省份数量。如果大区的表为area,省份表为province。
假设表的内容是这样:
可以得出sql语句如下:
-- 犯懒没加多余字段
select area.id,area.name,count(province.id)
from area left join province on area.id=province.areaId
group by area.id
然而得到的结果确是这样
可以看到,江北区是没有的
如果想要去掉,只需要添加一句:
where province.areaId is not null
那么这时候,还想要province的id,然后做下一步的处理,该咋办呢。
用到了一个函数是这样的
select area.id,area.name,count(province.id),group_concat(province.id)
from area left join province on area.id=province.areaId
where province.areaId is not null
group by area.id
最终能得到的结果就是
那么再深入一点,是都统计出来了。但是我还想筛选一下,看汉东省所属的大区,和这个大区下的所有省市,怎么办呢?
在sql优化的时候,有个原则叫小表驱动大表,而使用的关键字叫做exists,能不能利用一下呢?
然后就写出了这样的语句
select area.id,area.name,count(province.id),group_concat(province.id)
from area left join province on area.id=province.areaId
where province.areaId is not null
and exists (select 1 from province p where p.areaId=area.id and p.id=5)
group by area.id
可以先想象,有一个连接的大长表,就像上图那样。那么重点就是筛选,筛选什么?筛选包含汉东省的区域,并且这个区域就是大长表的id(area.id)。所以就是如果有一条记录证明区域和省份是有联系的,那就应该保存下来这个大长表的记录。exists中的语句可以返回任意结果,0,1,p.id……
那么这里可以再次使用left join
,而不是exists
达到目的么? maybe .
然而,这真的是没问题了么?曾经的我也是这么认为的,知道有一天……
想一下,我们选定了省份的id,想要统计和该省份有关系的区域的数量(在分页中,这是很常见的需求),那么我们是否可以将上面的sql语句简单用count(1)进行替换呢?就像下面这样
select count(1)
from area left join province on area.id=province.areaId
where province.areaId is not null
and exists (select 1 from province p where p.areaId=area.id and p.id=5)
group by area.id
然而结果是2。先看exists之前的语句执行结果,因为left join之后会产生笛卡尔积,所以就会产生两条记录,如下
那么这两条都满足 select 1 from province p where p.areaId=area.id(2) and p.id=5。而使用count(1)就是看有多少条数据满足要求,那么结果肯定也就是2了。那么该怎么解决呢,简单,distinct关键字就行了。最终解决方案
select count(distinct(area.id))
from area left join province on area.id=province.areaId
where province.areaId is not null
and exists (select 1 from province p where p.areaId=area.id and p.id=5)
group by area.id
或者是
select count(area.id)
from area left join province on area.id=province.areaId
where province.areaId is not null
-- and province.id= 5
group by area.id;
然后判断有多少个结果,所以查看总条数和查看每一条的内容,不是简单的替换啊。