我发现SQL存储过程非常有趣且有用。我已经编写了存储过程,但我想为任何类型的要求编写精心设计,性能良好的调优和简洁的SP,并且也希望了解存储过程的任何技巧或良好实践。在编写存储过程时如何从初学者转到高级阶段?
更新:从评论中发现我的问题应该更具体。 每个人都有一些技巧,我期待他们在他们的代码中使用SP的技巧和做法,使他们与其他人区别开来,更重要的是提高写作和使用存储过程的工作效率。
答案 0 :(得分:40)
以下是我的存储过程错误处理指南。
执行存储过程时,请始终检查@@ 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
在以下陈述后始终存储并检查@@ 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
答案 1 :(得分:17)
我总是尝试使用的唯一技巧是:始终在顶部附近的注释中包含示例用法。这对于测试SP也很有用。我喜欢包含最常见的示例 - 然后您甚至不需要SQL Prompt或单独的.sql文件与您最喜欢的调用,因为它存储在服务器中(如果您有存储的procs,那么它特别有用) sp_who输出块或其他什么,并采取一堆参数)。
类似的东西:
/*
Usage:
EXEC usp_ThisProc @Param1 = 1, @Param2 = 2
*/
然后,要测试或运行SP,只需在脚本中突出显示该部分并执行。
答案 2 :(得分:11)
这是一个非常普遍的问题,但这里有几条建议:
当然还有更多。这是一个更多的链接: SQL Server Stored Procedures Optimization Tips
答案 3 :(得分:11)
为:
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现在为您执行此操作,因此不会错过任何错误,因为没有错误。
答案 4 :(得分:3)
在SQL Server中,我总是放置一个语句,如果它存在,将删除该过程,因此我可以轻松地在开发过程中重新创建过程。类似的东西:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'usp') AND type in (N'P', N'PC')) DROP PROCEDURE usp
答案 5 :(得分:2)
这在很大程度上取决于您在存储过程中所做的事情。但是,如果在一个proc中进行多次插入/更新或删除,则最好使用事务。这样,如果一个部分失败,其他部分将回滚,使数据库保持一致状态。
写入数据库时要考虑的两个最重要的事情(因此当使用执行除select之外的操作的存储过程时)是数据完整性和性能。如果没有数据完整性,您只需拥有一个包含垃圾的数据库,并且无用。如果没有performacne,您将没有用户(如果他们在客户之外)或不满意的用户(如果他们被要求使用您的产品,通常是内部用户,他们别无选择去其他地方)。这对你的职业生涯都没有好处。因此,在编写存储过程时,请确保首先确保将数据正确输入数据库,并且如果操作的某个部分出现问题,则数据将失败。
如果需要在proc中写入检查以确保您的最终结果是正确的。我是ETL专家,在尝试将数据导入表格之前,我总是编写我的过程以使数据清理和规范化。如果你是从用户界面做的事情,这可能不是那么重要做在矿井PROC,虽然我会在用户inteface做检查,即使运行PROC,以确保数据之前是良好的插入(之类的检查,以确保日期字段包含实际日期,所有必填字段都有值等。)
如果您正在编写将大量数据放入表中的过程,那么最好有一种方法在最终确定之前测试这些结果。您会对客户和供应商提供的数据导入垃圾感到惊讶。我们使用测试标志编写所有导入过程。这样您就可以返回选择数据而不是执行操作,这样您就可以提前看到您将要影响的内容。
我不是动态SQL的粉丝,我不想在存储过程中使用它。如果您在现有的proc中遇到动态SQl,请输入一个调试标志,允许您打印SQL而不是执行它。然后在评论中添加您需要运行的最典型案例。如果你这样做,你会发现你可以更好地维护proc。
不要在游标中执行操作,因为您希望重用另一个仅在一条记录上运行的存储过程。如果是坏事,代码重用会导致性能问题。
如果您正在使用case语句或if语句,请确保已完成将触及每个可能分支的测试。你没有测试过的那个是失败的那个。
答案 6 :(得分:1)
这不是一个可以在没有更多信息的情况下直接回答的问题,但是一些一般的经验法则确实适用。
存储过程只是存储的T-SQL查询。因此,熟悉T-SQL以及各种函数和语法确实是您需要做的。从性能角度来看,您需要确保您的查询和底层数据结构以允许良好性能的方式匹配。 IE,确保在需要的地方实现索引,关系,约束等。
了解如何使用性能调优工具,了解执行计划的工作方式,以及具有这种性质的事情是如何实现“下一级”
答案 7 :(得分:1)
使用SQL Server 2008使用TRY ... CATCH构造,您可以在T-SQL存储过程中使用该构造,通过检查@@ ERROR,为SQL Server的早期版本提供更优雅的异常处理机制(并且经常使用GOTO语句)在每个SQL语句之后。
BEGIN TRY
one_or_more_sql_statements
END TRY
BEGIN CATCH
one_or_more_sql_statements
END CATCH
在CATCH块中,您可以使用以下错误函数来捕获有关调用CATCH块的错误的信息,
ERROR_NUMBER()
ERROR_MESSAGE()
ERROR_SEVERITY()
ERROR_STATE()
ERROR_LINE()
ERROR_PROCEDURE()
与由执行的每个语句重置的@@ error不同,错误函数检索的错误信息在TRY ... CATCH语句的CATCH块范围内的任何位置保持不变。这些函数可以将错误处理模块化为单个过程,因此您不必在每个CATCH块中重复错误处理代码。
答案 8 :(得分:0)
基本的东西:
制定错误处理策略,并在所有SQL语句中捕获错误 确定对存储过程使用源代码控制的策略 包含一个带有用户,日期/时间和sp目的的注释标题 成功执行时显式返回0(成功),否则返回其他内容 对于非平凡程序,包括测试用例(或案例)和预期结果的描述 养成性能测试的习惯。对于文本案例,至少记录执行时间 理解显式交易,并使用它们 几乎从不从SP调用SP。可重用性是与SQL不同的球赛。
答案 9 :(得分:0)
以下是一些最佳做法,
For more explanations and T-SQL code samples please check this post
答案 10 :(得分:0)
这里有一些代码可以证明SQL Server上没有多级ROLLBACK,并且它说明了如何处理事务:
BEGIN TRAN;
SELECT @@TRANCOUNT AS after_1_begin;
BEGIN TRAN;
SELECT @@TRANCOUNT AS after_2_begin;
COMMIT TRAN;
SELECT @@TRANCOUNT AS after_1_commit;
BEGIN TRANSACTION;
SELECT @@TRANCOUNT AS after_3_begin;
ROLLBACK TRAN;
SELECT @@TRANCOUNT AS after_rollback;