现在的位置: 首页 > 综合 > 正文

存储过程的编写经验

2012年12月09日 ⁄ 综合 ⁄ 共 3013字 ⁄ 字号 评论关闭

1.       结构

if exists (

 select * from sysobjects

 where id = object_id(N'[dbo].[np_DailyClose]')

    and OBJECTPROPERTY(id, N'IsProcedure') = 1)

drop procedure [dbo].[np_DailyClose]

GO

/****************************************************

       过程名称:np_DailyClose

       目的: 日结处理, 每日停业后必须做此工作,且只能做一次

       创建日期:             作者      
修改日期                修改人:                 内容:为采购退货暂估回冲单独增加处理程序

 返回代码:

    -1 该日期已经做过日结

    0 成功返回

 涉及表:

 1. tConfig

 讨论:

 1

 2

*****************************************************/

CREATE proc np_DailyClose (

 @dTransDate datetime -- 业务日期

)

as

return -- np_DailyClose

  

1.       结果的三种获得方法:可带回值的参数,return 值,select 返回记录集

2.       编写过程

1) 参数

2) 设置环境:显示设置一些环境开关:如加锁方法、set nocount on 等,否则因为用户环境不同,影响运行效果。

3) 框架:构思结构

4) 变量:统一放在前面,并注释。

5) 注释:注释和代码同步完成,保持一致。

6) 编写

7) 讨论记录

8) 测试

3.       必须考虑的问题

3.1     可读性

1) 逻辑清晰

2) 语句不要过于复杂,靠产生临时表过渡

3) 注释

4) 一屏可视,便于思考

5) 艺术感

3.2     效率

一种方法和语句的使用,必须考虑它的执行效率。注意以下问题:

1) 回避not in查询

2) 游标嵌套慎用

3) 使用索引:善于用索引,大大提高速度,在语句中有时需要显式说明。

4) 事务嵌套慎用。

结存程序一般30分钟以上,主要原因就是使用了not in 查询、游标嵌套、没有用索引。改造后同样数据量9秒就能完成。

4.       事务

1) 保持一致性所必需,必须有意识的考虑何时必须用事务。

2) 注意加锁特性,加锁属性开关状态。辽阳的例子,informix缺省加锁返回机制没有注意,造成偶尔死机,问题非常隐蔽。

3) 处理不当会带来系统死锁。

4) 仔细测试,否则的灾难将难以挽回。

5) 大量语句的事务推荐方法,将存储过程嵌在事务中,这样可以防止意外出错,造成死锁。见附件1

5.       测试

5.1     测试环境的搭建,要保存一个对应的程序,因为不会轻易结束。见附件3

5.2     编写和测试花一样的时间。

5.3     任何改动,都要测试。

5.4     叫真,不要想当然,因为还有系统软件的问题。如,错误号状态,是最后一个语句的错误,Sql 7.0bug : 语句“if @@Error <> 0,永远得不到想象的结果。

5.5     调式工具

1.       了解表的连接原理,能力进阶的关键

1)  灵活性,左连接,右连接。。。

2)  查询效率,条件语句的写法和顺序,自己掌握优化的主动权。

3)  保证正确,如update from累加修改时

       
update tMembers 

                     
set 

                            iShareA  
= a.iShareA + b.iNewShare 

                     
from vMembers a, (select * from vMembers) b

                     
where a.iMemberID = b.iUpMemberID 



       
update tMembers 

                     
set 

                            iShareA  
= a.iShareA + b.iNewShare 

                     
from tMembers a, (select * from vMembers) b

                     
where a.iMemberID = b.iUpMemberID 

区别:

使用视图时,左侧iShareA 和 右侧a.iShareA不是同一条记录;使用表时是一条。

iShareA  
= a.iShareA + b.iNewShare

2.       一些经验

2.1     善用临时表

1)避免多用户重名冲突。

2)  速度快,内存表。

3)  简化逻辑

4)  数据重用

2.2     回避not in 的技巧

  
--打标记bMark = 1(下一步对bMark = 0 的进行插入,这样速度快得多)

  
update #tWIV_Total set bMark = 1 

  
from

    #tWIV_Total a, tTWAccount b

  
where

    a.cWhsID 
= b.cWhsID  and a.cItemID = b.cItemID and a.cVendorID = b.cVendorID

 

  
-- 3)将bMark = 0 的关键字插入到tTWAccount中

  
insert into tTWAccount (cWhsID, cItemID, cVendorID, cCtrGrpID)  

  
select

    cWhsID,

    cItemID,

    cVendorID,

    
'' -- because not null 

  
from

    #tWIV_Total 

  
where

    bMark 
= 0

2.3     浮点数慎用

1)  会有芯片运算影响等问题

2)  累计求和时,舍入误差,经常产生“1分钱问题”。

3)  运行中产生意外结果,如判断 = 0,本来是0,可能判断结果不为0

2.4     统计数字作分母时,注意筛选条件加上<> 0限制,如

                     
select iLevel, count(*) iCount 

                            
into #tChainLevelCount

                            
from tMembers a, tChainLevel b

                            
where 

                                   a.iLevel 
= b.iID

                                   
and a.cState = '0' and b.cType = 'A' 

                            
group by iLevel

                            
having count(*> 0

抱歉!评论已关闭.