mysql存储过程:MySQL存储过程调试思路方法来源: 发布时间:星期日, 2009年8月16日 浏览:15次 评论:0
行不通办法
在存储过程中调试SQL:假设你存储过程主要逻辑是DDL和DML操作这个思路方法才能工作换句话说就是只有查询、插入等语句同时假设其它存储过程主要承担支持数据库操作在多数情况下这个假设是成立毕竟如果存储过程不操作数据库你也多半不会为此专门编写存储过程了 毫无疑问无论存储过程中包括了多少非SQL代码你都需要对SQL本身进行验证特别是这级别测试相对要简单些只需要启动数据库命令行工具或查询GUI浏览器将SQL语句粘贴进去执行下就知道是否正确没有当然这已经超出了简单语法正确性检查你必须亲自验证其语法正确性 但在某些情况下并是想象中那么简单原因有两个首先你SQL代码可以(通常都是“会”)依赖存储过程定义和/或操作变量和参数如果你有条SELECT语句它将它结果存储到个变量中后面SQL语句再来使用该变量那么这种粘贴到命令行测试思路方法可能就行不通了这个时候你不得插入条或多条语句执行它们也许还需要创建临时变量并且在真正测试时候可能还要修改SQL这种情况会逐渐发生但你可以达到某个点不用再从头开始测试了 使用这种思路方法第 2个问题是通常情况下存储过程逻辑是放在过程代码中而不是SQL语句中存储过程常常用于例子化业务逻辑通常是通过存储过程嵌入到代码流中在这种情况下只是对SQL语句进行下简单替补测试而不是真正测试存储过程 在存储过程中插入Pr语句 另个思路方法是在你存储过程中放置大量Pr语句这种做法在实际中非常有用特别是在早期开发阶段每个数据库都有其自己处理打印语句思路方法例如当你使用MySQL concat( )构建个串输出时你必须小心空值它将你整个串转为空值象下面这样代码就很危险: select someColumn from someTable o myVar where. concat('better hope myVar is not null', myVar); 如果where条件过滤结果为空(没有返回行数据)那么myVar可能就为空concat输出也就为空了因此最好使用concat_ws("delimiter", "text to store")它可以很好地处理空值 按照这种思路方法使用Pr语句有两个主要缺点首先在生产过程中Pr语句是活动除非你为每个Pr语句配个标签加以保护这就意味着如果你要直记录话就需要牺牲大量性能 第 2个缺点更严重如果你存储过程是从Java应用中Pr语句不会有任何动作只有从命令行执行存储过程时Pr语句才会被接受 制定严格返回代码 在这个思路方法中你需要为所有情况制定详细返回代码通过给定代码你就知道存储过程执行时发生情况了理论上这是个非常棒思路方法但在实际生产中可能会让人崩溃返回代码可能告诉你最后出错存储过程但更重要是要知道导致失败原因 换句话说如果你在凌晨3点接到个非常重要客户打来技术支持电话此时你肯定会希望客户告诉你返回代码好多 推荐思路方法 目前思路方法要使用多个MySQL特殊功能创建日志模式既强壮又自由首先我们创建两个表:个使用Memory引擎临时日志表个使用MyISAM引擎永久性日志表MySQL支持为区别表使用区别存储引擎MySQL存储引擎包括那些处理事务安全表和非事务安全表Memory引擎在内存中执行所有操作永远都不会写到磁盘上去因此它速度非常快但却是临时MyISAM引擎是个非事务安全引擎事务可以包括事务型和非事务型表(非事务型表忽略事务命令) 日志消息插入到tmplog表中表在内存中实际上它是个非常自由操作这个操作成本非常低因此开发人员可以很详细地使用日志记录不用再为是否要记录什么或不记录什么烦恼了 通常情况下不会发现什么问题存储过程没有做任何事情临时表也仅仅在连接时存在在典型J2EE使用模式中当个外部请求抵达系统中首先从连接池取出个连接然后又返回到连接池当连接返回到连接池时临时表就被删除了代码并未作出删除日志任何操作以这种方式系统几乎没有什么性能开销 当存储过程检测到故障时它会从临时内存表中向永久性MyISAM表中插入记录因此它将所有写入到临时表中日志消息都插入到MyISAM表了这样系统就记录了所有信息但只有在需要时候才需要用到 个重要值得注意事情是要为永久性日志表选择MyISAM引擎记住通常是事情变得糟糕时候才会向这个表写入数据这常常会导致当前事务回滚MyISAM引擎不是事务型引擎这意味着即使个事务回滚了插入到日志表中日志仍然保留下来了这正是期望行为 准则 在debugLogging.sql包中定义了4个存储过程其中两个用于创建将要用到表个执行临时日志记录最后个将日志消息拷贝到永久性表中 第个存储过程创建临时表和永久性表注意使用引擎类型以便区分这两个表类型临时日志表只包括个列msg永久性表添加了自动时间戳和thingID列假设在创建、破坏、修改某些对象期间写入了日志并且那些对象有唯标识符在我视频点播空间中它可能是流向客户机顶盒电影标识符 Create Procedure upLogging BEGIN create temporary table not exists tmplog (msg varchar(512)) engine = memory; create table not exists log (ts timestamp default current_timestamp, thingID big, msg varchar(512)) engine = myisam; END; 第 2个存储过程只创建临时日志表我们看到在我们需要插入数据时临时表不存在此时我们可以使用个存储过程来重新创建 Create Procedure upTmpLog BEGIN create temporary table not exists tmplog (msg varchar(512)) engine = memory; END; 第 3个存储过程是执行次数最多它真正用于记录日志个消息参数被写到临时表中如果临时表不存在这里提供了个继续创建临时表处理 Create Procedure doLog(in logMsg varchar(512)) BEGIN Declare continue handler for 1146 -- Table not found BEGIN call upTmpLog; insert o tmplog values('reup tmp table'); insert o tmplog values(logMsg); END; insert o tmplog values(logMsg); END; 最后个存储过程当用户存储过程中检测到时会它以及所有日志需要进行永久性写入便于以后分析时会它这个存储过程产生最后消息大概是有关原因是什么句话然后将临时表中所有记录插入到永久性表中并自动设置时间戳为了解决遇到时用户选择继续然后进入到下个情况当全部插入到永久性表后临时表中数据将全部删除 Create Procedure saveAndLog(in thingId , in lastMsg varchar(512)) BEGIN call dolog(lastMsg); insert o log(thingId, msg) (select thingId, msg from tmplog); truncate table tmplog; END; 使用举例 下面代码举例显示了个可能日志存储过程使用情况这个例子是基于个存储过程可以另个存储过程概念设计存储过程主要任务是解析个以逗号分隔值列表并将每个值插入到缓存Cache表中然后另个存储过程进行查询它介绍说明了值能够被记录而不仅仅是最终结果按照这个步骤循环下去就可以知道究竟是在哪步出现故障 Create Procedure parseAndStoreList(in thingId , in i_list varchar (128), out Code smallInt) BEGIN DECLARE v_loopIndex default 0; DECLARE Exit Handler for SQLEXCEPTION BEGIN call saveAndLog(thingId, 'got exception parsing list'); -- save the logs things go badly Code = -1; END; call dolog(concat_ws('got list:', i_list)); -- say we got to the start pase_loop: LOOP v_loopIndex = v_loopIndex + 1; call dolog(concat_wc(',', 'at loop iteration ', v_loopIndex); -- say we got to nth iteration -- actually do the parsing, or whatever END LOOOP parse_loop; Code = 0; END; 小结 无开销或开销极低存储过程日志记录被证明是种极其有用技术你可以利用它对代码进行广泛检查可以讲起运用到生产环境但建议当你确实需要时才运用 0
相关文章
读者评论发表评论 |