开始
前几天有个同事告诉我说,他发现有个外包人员在他负责的项目里下毒,写了一堆让人烦躁的 sql,改都改不掉,一开始就跟那人说了不要写复杂 sql 不要写复杂 sql,还是写了,妈的,气死了。
为什么同事这么火大,用脚想也知道,复杂的 sql 让人难以维护,但是仅仅是难以维护这么简单吗?显然不是,真相远远没有你想象的那么简单。
事实上,公司几乎都让开发人员用 orm 单表查询,然后在代码层逻辑组装,禁止开发人员写联表 sql 查询。
下面我来分析一波:
对比
1.关联查询
在实际项目开发中,前端一个列表要展示的数据往往不是来源于单表,所以我们经常要关联多表进行查询,类似下面这样:
select a.id,a.staffName,b.orgName,c.roleName
from
staff a
join org b on a.org_id = b.id
join role c on a.role_id = c.id
where a.name = "喝水"
2.单表查询+代码层逻辑组装
但是,我们也可以分三次单表查询,在代码中组装前端需要展示的字段。伪代码:
// 1.先按条件过滤查询staff
select * from staff where name = "喝水";
// 2.从步骤1查询的结果中拿到org_id和role_id的集合,
比如org_id的集合为(1,2),role_id的集合为(1,2,3)
//3.单表查询org。并得到map<orgId,orgName>
select * from org where id in (1,2);
//4.单表查询role。得到map<roleId,roleName>
select * from role where id in (1,2,3);
//5.遍历步骤1查询的结果,冗余前端需要展示的orgName和roleName字段
表面看起来,单表查询+代码层逻辑组装的方式,不但查询了三次数据库,代码层还多了组装逻辑的代码。你肯定会问,这不是复杂化了吗?其实这种方式有许多好处:
好处
1.代码复用
关联查询的 sql 基本没法复用,但是拆分成单表查询,就像是一个个积木,其他地方需要都可以复用这段代码。
2.利于后续维护
上面的业务场景可能还看不出来,如果关联查询很复杂,写的 sql 可读性必将很差,过了一段时间自己都会看不懂,更不用说后续接手你的兄弟。
再一个后续业务如果变动,导致表的结构发生变化,原先写的联表查询 sql 也会变得不可用。但是我们如果是使用单表查询,这时候只需要修改其中一个查询,非常利于维护。
3.效率
实际上 mysql 并不推荐使用 join 和子查询去实现复杂查询。
join 联表查询,会自动优化为小表驱动大表,通过索引字段进行关联。如果表数据量较小的话效率还是可以的。
多表关联查询是笛卡尔乘积的方式,数据量一旦上去,需要检索的数据是以几何倍上升的。另外多表关联查询的索引设计也要好好考虑,如果索引设计的不合理,大数据量下的多表关联查询,很可能让数据库拉垮。
相比之下,用单表查询+代码层逻辑组装的方式,业务逻辑更清晰,优化维护更方便,单表索引的设计也更简单,大数据量下的查询效率更高。如此说来,多几行代码,多几次数据库查询可以换取这些优点,还是挺不错的。
子查询就更不用谈,效率极差。因为执行子查询时,mysql 需要创建临时表,查询完成后再删除临时表,这里多了一个创建和删除临时表的步骤,所以子查询的效率会受到影响。
4.可扩展
当数据量大到一定程度,join 查询不利于分库分表,目前 mysql 的分布式中间件,对于跨库 join 的表现来看并不好。
而拆分为单表查询+代码层做冗余处理,可以更容易对数据库进行分库分表,更容易做到高性能和可扩展。
总结
一个系统的瓶颈往往是出在数据库上,所以不要在数据库中做业务逻辑处理,建议数据库只是作为数据存储的工具。




