最近我发现在上次Release中,由于时间太紧,个人水平有限,在许多存储过程中用了这么一个子查询,用以过滤用户没有权限访问的交易业务,类似这种形式:
DECLARE @IDs TABLE(GroupID INT)
--填充该用户无权限的组ID
SELECT * FROM Deals WHERE GroupID NOT IN (SELECT GroupID FROM @IDs)
当时加上这个子查询时,因为前面还有很多连接操作,认为慢也正常。但是当我要将这段逻辑扩展到其他功能上时,我犹豫了,有些很常用的查询也要等个近一分钟,就算用户能忍受,我自己都接受不了,于是我试着把(SELECT GroupID FROM @IDs)换成(3,4,5,...)这种直接数字。结果一试,大吃一惊,原来时间几十秒的执行,一秒内跑完!
自己太天真了,原来SQL Server根本不会去优化子查询,将结果缓存下来并内联。
不想用动态SQL,于是google一下,找到子查询(in not )转化为连接查询的方法,上面语句转化为:
SELECT * FROM Deals d
LEFT JOIN @IDs i ON i.GroupID = d.GroupID
WHERE i.GroupID IS NULL
也基本是一秒内完成。
了解这个后,心情又沉重又悲痛。悲痛的是我工作了这么多年,SQL还是菜鸟,而且同事中没有一个能指出来这么简单的问题。沉重的是从SQL Server 2000到2012,还是老样子,除了不能优化子查询,还是定义集合变量,依然笨拙的聚合操作,可怜的SQL复用性支持,难于调试。其实,不只是SQL Server,这是所有关系型数据库,积重难返的通病,是已经严重滞后的SQL语言对生产力的巨大桎梏。
在我经历以数据驱动的系统,可能SQL开发代码量只占一小部分,但其工作量要数倍于面向对象语言,而且Bug的集中营,还集中了几乎全部系统性能的勒索。SQL不但谋杀程序员的青春,更绑架了无数程序员的思想,不会学习新技术,不会复用代码,不会单元测试,就是被SQL绑架的程序员。
在一个崭新的系统你们不会觉得,这时数据库像一个温顺可爱的小猫,但随着它一天天长大,数据一天天增加,你会发现它变成了一头可怕的老虎,不是只有程序员,还有用户,部署人员,DBA,都要小心翼翼的伺候着。
看着被吃掉的时间和金钱,我们不能再养虎为患了。那么我们现在应该怎么做呢?
小型服务器系统和中小型客户端系统,比如手机上的一个应用,仍然可以用关系型数据库,但一定要把它放在ORM的笼子里,而且要提防它长大。
区别是否该用RDBMS,有一个直观标准可以作参考,就是用Visual Studio或SharpDevelop中的Entity Model设计器,如果对象排满了一页,关系七零八落看不清楚,就该考虑No-SQL数据库。
对于大型客户端系统,应该用强对象型数据库,比如db4o。对于中大型服务器系统,应该采用分布式对象数据库,比如MongoDB。
或者,根据系统性能需要,开发专门的存储格式。
对于巨型服务器系统,比如Google/Facebook,除了Hadoop这类分布文件系统,没有第二选择。
那种几百个表,几千个存储过程的系统,是时候进入历史的垃圾堆了。我还算庆幸,手头这个项目再用一年左右就会淘汰了,如果你的现在的工作或新工作也是这种项目,请好好考虑一下自己的职业发展吧。