SQL,SP与ORM

时间:2022-05-12 15:51:06
SQL译为按每一次情况的办理,SP意为存储过程,ORM就是对象-关系映射,比如Hibernate
一,演变
  刚开始的时候,只有sql语句,即可以用交互模式一句一句执行, 也可以用批模式执行,多行sql语句一次提交执行。
  很快人们发现用批模式执行的一堆sql语言可以用过程的形式,事先存放到数据库里面,这就变成了存储过程。
  随着面向对象技术的成熟,从程序中可以自动生成sql语句,这就是ORM

二,性能
  如果我们把一堆sql,以批的方式一次送入到服务器,那么服务器,会对这一堆sql进行缓存,当下一次再度执行的时候,就好像调用一个”匿名“的存储过程一样。在这种情况下,性能差不多。
  但是,如果我们不注意,很有可能,把可以一次提交的sql,变成了多次提交,甚至是每个循环做了一次提交,那么性能就很差了,也就是说如果使用sql,只要写法得当,性能和sp区别不大。
  同样的道理,ORM的性能取决于ORM的Sql生成算法, 和用户使用的时候,对生成算法的控制,比如利用好Lazy laoding等,在某些情况下,甚至可以不通过sql,毕竟没有sql比最优化的sql还要快。

三,可维护性

  可维护性是选择sql,sp,orm最主要的因素。
  刚开始的时候,sql的维护性看起来是最差,因为它往往散布在程序的每个角落。而存储过陈都放在数据库中,有清晰接口。
  但是如果我们做一次重构,情况居然会颠倒过来。
  首先,存储过程完全可以照搬到C#中,sp的名字直接变成method的名字,sp的参数表直接变成method的参数表,(其实就是Command模式)。
  其次,把这些methdod放到一个文件或者文件夹中。(所谓的DAL层,如果喜欢层的话)
  通过这个重构,我们获得了以下的好处,

1,首先是过程的调用和过程的定义放到了一起,修改起来比较方便。IDE都有定义跳转功能。
  2,过程的调用和定义同时进行版本控制,不会出现不匹配的情况。减少了sp的参数表和调用的不匹配,包括拼写,类型,参数次序
  3,单元测试非常方便

当然sp也有存在的价值,比如所谓的安全性,后面会提到。比如友好的调试环境,对于中小型项目,和初级程序员来说,也是很好的选择。  
  ORM则将可维护性提升身到了一个新的高度,它试图将sql屏蔽起来,在操作对象的同时,自动就把数据库的事情给办了。
  ORM有两种模式,一种是ActiveRecord, 一种是Datamapper,前者从数据库中读取定义,后者在程序中定义。不过由于前者往往用migration来生成数据库,其实也是定义在程序里面的。好的ORM都有"leaking"的设计,也就是留了个”后门“,让你有机会用sql来控制。  
  微软的linq从某个角度类说,也是一种ORM, 它的设计思想可能是因为它觉得写sql语句比写c#代码效率高,所以提供直接在C#中写sql语句的机制,再自动生成真正的sql。不过,ORM真正价值在于它可以在恰当的时候,完全抛弃sql,比如比如读用cache,写用queue。而微软的linq,完全是“无厘头”的风格,在O中用R的写法,难道是RRM, 唯一的好处只是锁定程序和程序员在微软的平台上。

 三,安全性

对企业来说,安全性有的时候比性能更重要,由于存储过程在数据库上多加了一道屏障,所以很多企业会把存储过程作为首选。
  ORM可以说是安全性最差的, 因为只有到程序运行起来,你才能知道,会产生什么样的sql。
  但是保证安全有许多方法和方面,比如部署前的测试, 数据库的备份,对表的权限的设置。等。用sp来保证安全,只是多个选项中的一个。
  在startup型企业中,高级程序员往往起到主导作用, 所以他们会不犹豫的选择ORM。
  在传统企业中,如果DBA或者技术主管比较强势,往往会采用sp。

综上所述:

  ORM SQL
开发速度
运行性能
方便修改    
可维护性

ORM,最大的问题是复杂查询,可以考虑SQL和ORM两者同时使用,简单地方用ORM,复杂地方用SQL;利用Cache等技术可以很好地提高查询速度,在Phalcon中,就是采用两者合用的方式:

1.Model中返回的是当前的Model集
2.PHQL中返回的是查询集
前者支持魔术方法get/count加关联Model名,用可选参数来进行二次筛选;后者采用类似SQL的方法来关联表,这样查询集
里面就利用驼峰规则就可以访问所有的字段:
$phql = "SELECT Cars.*, Brands.* FROM Cars, Brands WHERE Brands.id = Cars.brands_id"
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo "Car: ", $row->cars->name, "\n";
    echo "Brand: ", $row->brands->name, "\n";
}
还可以访问指定字段:
$phql = "SELECT Cars.name AS car_name, Brands.name AS brand_name WHERE Brands.id = Cars.brands_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->car_name, "\n";
    echo $row->brand_name, "\n";
}
当然可以加别名访问:
$phql = "SELECT c.*, b.* FROM Cars c, Brands b WHERE b.id = c.brands_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo "Car: ", $row->c->name, "\n";
    echo "Brand: ", $row->b->name, "\n";
}