该不该在项目中使用存储过程代替SQL语句
存储过程的好外,我就不多说了,想必各位都已了然于胸
当然,存储过程也有不少坏处:
1,当存储过程数量越来越多的时候,在众多存储过程中找到想要修改的存储过程是一件麻烦的事.
2.如果用嵌入式SQL语句,可以在修改代码时,顺便就修改了数据库操作语句,方便
针对这两个所谓的缺点,我提出我的一些看法:
1.如果说存储过程多了,不好找,那你该检讨一下您的命名习惯是否规范是否达意,如果是多人合作的团队,大家更应该对于存储过程的命名有一致的规则,当然,不只存储过程需要这样,其他部分也都要需要这样.好的存储过程命名最好能包含操作名称(insert/update/get/list等),要操作的对象名称(表名)等,这样,即便你的存储过程再多,一样也能快速找到要改的那个,这样命名,还可以让你通过SQL 2000的对象查找功能一次性的按表名找到与此表相关的所有存储过程的名称,同理,你用LIST来查,也可以查到所有LIST功能的存储过程
2,对于第二种观点,我是不大同意的,在过往的例子中,我发现,将SQL语句从代码中分离出来,带来的好处远远大于坏处,而且这样更符合分层的原则,如果我们将SQL语句嵌入到代码中,当你仅需要多获取一个字段的值,或者对SQL语句本身做一些修改时,你就必须要编译,然后上传DLL,而如果你是用存储过程的话,你直接改一下存储过程就好了,而且,将二者分离,DBA写好存储过程,列好说明及使用规则,交给负责写DAL层的同学,DAL层的同学闭上眼无需了解SQL语句,也可完成他的工作,因此,从这个角度来说,很好的分隔了工作,不必要要写DAL层的同学也是SQL存储过程高手了
3,防止注入攻击,如果不用存储过程而用嵌入式SQL,你势必要为了防止注入攻击而对输入的用户数据做更多的处理工作,例如处理一些SQL敏感字符等
4.更为重要的是,如果你要朝一个表中插入的是一个BINARY内容的时候,难道你会用SQL语句吗?
5,嵌入式SQL特别是拼贴SQL语句,一向是比较容易出问题的环节,而存储过程在写的时候,就经过检查,储如漏掉符号,INSERT的字段数目与参数数目不一致的小错误,会立即被纠正
6,谁都知道存储过程是预编译的
7,如果你是高手,你可以分析并优化存储过程来提高性能(以前记得看过MS的一个牛人技术支持讲述存储过程分析和优化,非常启发人)
最常见的是,在实际运用中,为了减少DATASET数据集的大小和提高性能,通常我们只SELECT当前需要的字段,但是,随着发展,你可以需要其他字段,这时,如果用嵌入SQL,就要修改SQL语句,编译,再写上绑定该字段的表达式,但是,如果用存储过程,你只要绑定表达式,然后给存储过程中加上这个字段名就可以了.
再如,如果用STRING来拼贴SQL的INSERT语句,那很可能是这样拼
string strSql="insert into table (id,username,password,address) value ("+Id.ToString()+","+UserName...
这样拼贴,多加个字段时,一花眼,就拼贴错了
如果用存储过程,你顶多用
SqlParameter myPara=new SqlParameter("@field5",Field5);
再在存储过程里加上这个输入参数就可以了,和修改一下SQL语句就行了,SQL还会在修改过程中帮你检查语法
后者显然比前者用那么多+号与双引号拼贴出错的几率小多了
最后,以上观点仅体现个人观点,不过,绝不是书上看来的,而是自己做了几个项目,边做边体会到的
因为最近工作接触到一个系统,业务过程几乎完全是用存储过程实现的。随着系统的不断发展,新的需求逐渐难以支持。这个原因当然很复杂,即使不使用存储过程,可能也有同样的问题。但是既然谈到具体技术上,就来看一下一个主要以存储过程实现的系统到底有哪些问题。
存储过程和嵌入程序中的SQL哪个更好,要用一种合理的比较方式来比,不能拿写的好的存储过程和写的烂的程序比,当然也不能拿写的烂的存储过程和写的好的程序比。我们先假设开发人员具有同样水平,项目组具有同样的组织协调能力,他们写出的存储过程和代码具有同样的质量,都已经根据产品的具体情况做出了最优的选择。
很多人这样认为:存储过程运行在最靠近数据的地方,最大限度减少了业务处理的环节,因此具有最高的运行效率。对于一个独立的程序片断来说确实如此。但是当一个软件规模逐渐增大,业务逻辑逐渐变得复杂以后,这一点差距已经不会对运行效率造成决定性的影响了。这时候,影响程序效率的因素变的更加复杂,比如:系统对于并发任务的处理是否合理、是否具有分布式的能力、是否可以将常用的数据缓存……这些能力靠存储过程来实现是非常难的,甚至是不可能的。一旦采用了存储过程,在程序漫长的生命周期中,要提高程序的运行效率就只有一个办法了:增加硬件投资。但是这个办法不一定有很好的效果,因为再好的硬件条件也无法弥补一些根本的缺陷。
软件开发发展到现在,总的来说有个规律,就是要让开发者越来越少的考虑技术问题,越来越接近客户的业务思维。现代的软件开发,已经越来越接近这样的方式:研究客户的需求,为业务建立模型,分析系统的外部和内部需求,以最接近业务模型的方式建立软件系统模型。这样的方式建立的系统才能最好的满足客户的需要,同时也能较好的适应需求的发展。
如果采用存储过程作为建立业务层的形式,结果就是回到“排列需求——数据库设计——界面设计——编码——测试”的道路上。这样当然是可以把系统做出来的。系统部署了,这只是他生命周期的开始,一切才刚开始。存储过程不仅实现了当前的业务需求,也建立了一系列的API。在漫长维护过程中,维护人员在这些API的基础上实现新的需求、修改这些API的错误。如果发现某个API“似乎”错了,或者不满足新的业务需求,没有人敢修改他们,最好的办法是:再加一个新的API。存储过程的数量越来越多,越来越难以命名——函数难命名不是编码的问题,而是设计的问题。于是,在项目运行两年以后,还是难以形成一个优质稳定的业务开发平台,人们还在探究数据表、字段、VARCHAR500、1403 data not found……
说到这里我也许应该得出结论,存储过程是不好的。可以这么说:用存储过程实现主要业务逻辑不是一个好办法。但是这个东西既然被创造出来,一定也有他适用的地方。
如果我有这样的需求:数据库中有大量的实时运行数据,需要定期把这些数据进行简单的行列归整,放到另外一个数据库中做分析统计之用。在这种情况下我首选存储过程。存储过程应该处理“数据”,而不要处理“业务”。并且在这种情况下存储过程极大的减少了IO消耗,真正的体现了他的效率优势。
下面张贴一个存储过程,会发现很多毛病
create or replace procedure P_GENERATE_TASK_INSTANCE(MOD_RW_XH in Pt_Zbmx_Fxtz_Mod.RW_XH%TYPE) as
--根据任务模版序号 产生任务实例
new_rw_xh pt_zbmx_fxtz.rw_xh%type; --任务序号
row_count number;
v_zqpl varchar2(2); --周期频率
current_year number; --当前年
current_month number; --当前月
current_bn number; --当前半年
v_ywfc_dm pt_zbmx_fxtz.ywfc_dm%type;
v_month NUMBER; --周期值 月
v_day NUMBER; --周期值 日
v_time CHAR(8); --周期值 时间
plan_time date; --计划执行时间
new_rw_mc VARCHAR2(80); --任务名称
begin
--获取当前时间
current_month := f_date_qy(sysdate);
current_year := f_date_qn(sysdate);
if (current_month >= 7) then
current_bn := 2; --下半年
else
current_bn := 1; --上半年
end if;
--获取周期频率 及各项值
select zqpl, month, day, time
into v_zqpl, v_month, v_day, v_time
from pt_zbmx_fxtz_mod_zq
where rw_xh = MOD_RW_XH;
--获取任务名称
select rw_mc
into new_rw_mc
from pt_zbmx_fxtz_mod
where rw_xh = MOD_RW_XH;
--获取新的任务序号
new_rw_xh := f_get_dm('rw_xh');
--获取业务范畴代码
select ywfc_dm into v_ywfc_dm from pt_zbmx_fxtz_mod where rw_xh = MOD_RW_XH;
--根据模版产生新的 fxtz(分析台帐) 任务
insert into pt_zbmx_fxtz
(rw_xh,
mx_dm,
rw_mc,
rw_zt,
sm,
czsj,
czy_dm,
fxdxlb,
ywfc_dm,
xays,
fxjh,
xchs,
xcbl,
rgsxhs,
ywths,
mxmzl,
fsbz,
fxys,
bcys,
dfys,
ljys,
kssj,
jssj,
rwlx,
rwly,
rwzxlx)
(select new_rw_xh,
mx_dm,
rw_mc,
rw_zt,
sm,
czsj,
czy_dm,
fxdxlb,
ywfc_dm,
xays,
fxjh,
xchs,
xcbl,
rgsxhs,
ywths,
mxmzl,
fsbz,
fxys,
bcys,
dfys,
ljys,
kssj,
jssj,
'1',
'1',
'1'
from pt_zbmx_fxtz_mod
where rw_xh = MOD_RW_XH);
--将模版条件表中的数据复制到fxtz_tj表中 除了PT_DIM_ZQ记录
insert into pt_zbmx_fxtz_tj
(rw_xh, tjmc, dim_table, dim_field, tjf, tjz)
(select new_rw_xh, tjmc, dim_table, dim_field, tjf, tjz
from pt_zbmx_fxtz_mod_tj
where rw_xh = MOD_RW_XH
and dim_table <> 'PT_DIM_ZQ');
--获得tj表中PT_DIM_ZQ记录的条数
select count(*)
into row_count
from pt_zbmx_fxtz_mod_tj
where rw_xh = MOD_RW_XH
and dim_table = 'PT_DIM_ZQ';
--向tj表中增加YEAE的记录
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_YEAR', '=', f_date_qn(sysdate), 'YEAR');
if (row_count = 0) then
--模版TJ表中没有关于ZQ的记录 向TJ表插入BN记录
if (v_zqpl = 'BN') or (v_zqpl = 'J') or (v_zqpl = 'Y') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_BNBZ', '=', current_bn, 'BN');
end if;
--模版TJ表中没有关于ZQ的记录 向TJ表插入JD记录
if (v_zqpl = 'J') or (v_zqpl = 'Y') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_JDBZ', '=', f_date_qj(sysdate), 'JD');
end if;
--模版TJ表中没有关于ZQ的记录 向TJ表插入Y记录
if (v_zqpl = 'Y') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_YF', '=', current_month, 'YF');
end if;
end if;
--记录条数是一的只插YEAE记录
-- if (row_count = '1') then
-- end if;
--插BN记录
if (row_count = '2') or (row_count = '3') or (row_count = '4') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_BNBZ', '=', current_bn, 'BN');
end if;
--插JD记录
if (row_count = '3') or (row_count = '4') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_JDBZ', '=', f_date_qj(sysdate), 'JD');
end if;
--插Y记录
if (row_count = '4') then
insert into pt_zbmx_fxtz_tj
(rw_xh, dim_table, dim_field, tjf, tjz, tjmc)
values
(new_rw_xh, 'PT_DIM_ZQ', 'ZQ_YF', '=', current_month, 'YF');
end if;
--根据周期频率和周期表的月份,天,时间的值组织Plan_time
--周期频率是 N plan_time等于当前年的第一天
if (v_zqpl = 'N') then
plan_time := f_date_nyrsjhc(current_year, v_month, v_day, v_time);
end if;
--周期频率是 BN plan_time=当前年 + 半年的月份值+天+时间
if (v_zqpl = 'BN') then
plan_time := f_date_nyrsjhc(current_year,
(current_bn - 1) * 6 + v_month,
v_day,
v_time);
end if;
--周期频率是 J plan_time=当前年 + 季度的月份值+天+时间
if (v_zqpl = 'J') then
plan_time := f_date_nyrsjhc(current_year,
(f_date_qj(sysdate) - 1) * 3 + v_month,
v_day,
v_time);
end if;
--周期频率是 Y plan_time=当前年 + 当前月+天+时间
if (v_zqpl = 'Y') then
plan_time := f_date_nyrsjhc(current_year, current_month, v_day, v_time);
end if;
--向任务实例表中插入实例 plan_time就是上面组合的时间 task_prop=FXXA,task_from=1,execute_type=1,task_type=1,status='00',
insert into pt_task_instance
(task_id,
task_name,
task_prop,
task_from,
task_type,
execute_type,
plan_time,
status,
mod_task_id,
generate_time,
ywfc_dm)
values
(new_rw_xh,
new_rw_mc,
'FXXA',
'1',
'1',
'1',
plan_time,
'00',
MOD_RW_XH,
sysdate,
v_ywfc_dm);
commit;
end;
没有评论:
发表评论