我发现SQL存储过程非常有趣和有用.我已经编写了存储过程,但我想为任何类型的要求编写精心设计,性能良好的调优和简洁的SP,并且也希望了解存储过程的任何技巧或良好实践.如何在编写存储过程时从初学者升级到高级阶段?
更新:从评论中发现我的问题应该更加具体.每个人都有一些技巧,我期待他们在他们的代码中使用SP的技巧和做法,使他们与其他人区别开来,更重要的是提高写作和使用存储过程的工作效率.
这是我的存储过程错误处理指南.
使用其完全限定名称调用每个存储过程以提高性能:即服务器名称,数据库名称,架构(所有者)名称和过程名称.
在创建每个存储过程的脚本中,显式指定允许执行过程的角色,例如public或者其他.
使用sysmessage,sp_addmessage和占位符而不是硬编码的错误消息.
使用sp_addmessage和sysmessages时,请始终使用错误消息编号50001或更高版本.
使用RAISERROR时,始终为警告消息提供<= 10的严重性级别.
使用RAISERROR时,始终为错误消息提供11到16之间的严重性级别.
请记住,使用RAISERROR并不总是中止正在进行的任何批处理,即使在触发器上下文中也是如此.
在使用或查询之前将@@ error错误保存到局部变量.
在使用或查询之前,将@@ rowcount保存到本地变量.
对于存储过程,请使用返回值仅指示成功/失败,而不是任何其他/额外信息.
存储过程的返回值应设置为0表示成功,非零表示失败.
将ANSI_WARNINGS设置为ON - 这将检测任何聚合分配中的空值,以及超出字符或二进制列的最大长度的任何分配.
设置NOCOUNT ON,原因有很多.
仔细考虑是否要XACT_ABORT ON或OFF.无论你走哪条路,都要保持一致.
退出第一个错误 - 这实现了KISS模型.
执行存储过程时,请始终检查@@ error和返回值.例如:
EXEC @err = AnyStoredProc @value SET @save_error = @@error -- NULLIF says that if @err is 0, this is the same as null -- COALESCE returns the first non-null value in its arguments SELECT @err = COALESCE( NULLIF(@err, 0), @save_error ) IF @err <> 0 BEGIN -- Because stored proc may have started a tran it didn't commit ROLLBACK TRANSACTION RETURN @err END
执行导致错误的本地存储过程时,请执行回滚,因为该过程可能已启动它未提交或回滚的事务.
不要以为只是因为你没有启动一个事务,没有任何活动的事务 - 调用者可能已经启动了一个事务.
理想情况下,避免在调用者启动的事务上进行回滚 - 因此请检查@@ trancount.
但是在触发器中,总是进行回滚,因为您不知道调用者是否发起了活动事务(因为@@ trancount总是> = 1).
在以下声明后始终存储并检查@@ error:
INSERT, DELETE, UPDATE SELECT INTO Invocation of stored procedures invocation of dynamic SQL COMMIT TRANSACTION DECLARE and OPEN CURSOR FETCH from cursor WRITETEXT and UPDATETEXT
如果DECLARE CURSOR在进程全局游标(缺省值)上失败,则发出语句以释放游标.
小心UDF中的错误.当UDF中发生错误时,函数的执行会立即中止,调用UDF的查询也会中止 - 但@@错误为0!在这些情况下,您可能希望使用SET XACT_ABORT ON运行.
如果要使用动态SQL,请尝试在每个批处理中只有一个SELECT,因为@@ error仅保存最后执行的命令的状态.一批动态SQL中最可能出现的错误是语法错误,SET XACT_ABORT ON不会处理这些错误.
我总是尝试使用的唯一技巧是:始终在顶部附近的注释中包含示例用法.这对于测试SP也很有用.我喜欢包含最常见的示例 - 然后您甚至不需要SQL Prompt或单独的.sql文件与您最喜欢的调用,因为它存储在服务器中(如果您有存储的procs查看,这是特别有用的sp_who输出块或其他什么,并采取一堆参数).
就像是:
/* Usage: EXEC usp_ThisProc @Param1 = 1, @Param2 = 2 */
然后,要测试或运行SP,只需在脚本中突出显示该部分并执行即可.
这是一个非常普遍的问题,但这里有几条建议:
一致地命名存储过程.许多人使用前缀来标识它是一个存储过程,但不要使用'sp_'作为为主数据库指定的前缀(无论如何在SQL Server中)
设置NOCOUNT,因为这会减少可能的返回值的数量
基于集合的查询通常比游标执行得更好.这个问题更加详细.
如果您为存储过程声明了变量,请使用良好的命名约定,就像在任何其他类型的编程中一样.
使用其完全限定名称调用SP以消除应该调用哪个SP的混淆,并帮助提高SQL Server性能; 这样可以更容易地找到有问题的SP.
当然还有更多.以下是更多链接: SQL Server存储过程优化提示
始终使用SET NOCOUNT ON
如果要执行两次或更多次插入/更新/删除,请使用事务.
永远不要命名你的过程'sp_'.SQL Server将首先查看master数据库,而不是找到它,然后再查看数据库.如果以不同的方式命名proc,SQL Server将首先查看您的数据库.
坏:
SET NOCOUNT ON BEGIN TRAN INSERT... UPDATE... COMMIT
更好,但看起来凌乱,代码的主要痛苦:
SET NOCOUNT ON BEGIN TRAN INSERT... IF @ErrorVar <> 0 BEGIN RAISERROR(N'Message', 16, 1) GOTO QuitWithRollback END UPDATE... IF @ErrorVar <> 0 BEGIN RAISERROR(N'Message', 16, 1) GOTO QuitWithRollback END EXECUTE @ReturnCode = some_proc @some_param = 123 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback COMMIT GOTO EndSave QuitWithRollback: IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION EndSave:
好:
SET NOCOUNT ON SET XACT_ABORT ON BEGIN TRY BEGIN TRAN INSERT... UPDATE... COMMIT END TRY BEGIN CATCH IF (XACT_STATE()) <> 0 ROLLBACK END CATCH
最好:
SET NOCOUNT ON SET XACT_ABORT ON BEGIN TRAN INSERT... UPDATE... COMMIT
那么"最佳"解决方案的错误处理在哪里?你不需要任何.请参阅SET XACT_ABORT ON,这意味着如果有任何错误,请执行自动回滚.代码更清晰,更易于阅读,更易于编写,并且减少了错误.由于SQL Server现在为您执行此操作,因此不会错过任何错误.