Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql ";对",;执行存储过程参数验证的方法_Sql_Sql Server_Stored Procedures_Error Handling - Fatal编程技术网

Sql ";对",;执行存储过程参数验证的方法

Sql ";对",;执行存储过程参数验证的方法,sql,sql-server,stored-procedures,error-handling,Sql,Sql Server,Stored Procedures,Error Handling,我有一个存储过程,它执行一些参数验证,如果参数无效,则应该失败并停止执行 我的第一种错误检查方法如下所示: create proc spBaz ( @fooInt int = 0, @fooString varchar(10) = null, @barInt int = 0, @barString varchar(10) = null ) as begin if (@fooInt = 0 and (@fooString is null or @fooString = ''))

我有一个存储过程,它执行一些参数验证,如果参数无效,则应该失败并停止执行

我的第一种错误检查方法如下所示:

create proc spBaz
(
  @fooInt int = 0,
  @fooString varchar(10) = null,
  @barInt int = 0,
  @barString varchar(10) = null
)
as
begin
  if (@fooInt = 0 and (@fooString is null or @fooString = ''))
    raiserror('invalid parameter: foo', 18, 0)

  if (@barInt = 0 and (@barString is null or @barString = ''))
    raiserror('invalid parameter: bar', 18, 0)

  print 'validation succeeded'
  -- do some work
end
这并没有起作用,因为严重性18不会停止执行,“验证成功”与错误消息一起打印

我知道我可以简单地在每个错误后添加一个返回,但这看起来有点难看:

  if (@fooInt = 0 and (@fooString is null or @fooString = ''))
  begin
    raiserror('invalid parameter: foo', 18, 0)
    return
  end

  ...

  print 'validation succeeded'
  -- do some work
由于严重性为11或更高的错误是在try/catch块中捕获的,所以我测试的另一种方法是将错误检查封装在这样的try/catch块中。问题是错误被忽略了,根本没有发送到客户端。所以我做了一些研究,找到了解决错误的方法:

  begin try
    if (@fooInt = 0 and (@fooString is null or @fooString = ''))
      raiserror('invalid parameter: foo', 18, 0)

    ...
  end try
  begin catch
    exec usp_RethrowError
    return
  end catch

  print 'validation succeeded'
  -- do some work
我仍然不满意这种方法,所以我想问你:


您的参数验证看起来如何?是否有某种“最佳实践”来进行此类检查?

我认为没有一种“正确”的方法可以做到这一点

我自己的偏好与第二个示例类似,但每个参数都有一个单独的验证步骤和更明确的错误消息

正如您所说,这有点麻烦和丑陋,但是代码的意图对任何阅读它的人来说都是显而易见的,并且它完成了工作

IF (ISNULL(@fooInt, 0) = 0)
BEGIN
    RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0)
    RETURN
END

IF (ISNULL(@fooString, '') = '')
BEGIN
    RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0)
    RETURN
END

我们通常避免使用raiseerror()并返回指示错误的值,例如负数:

if <errorcondition>
    return -1
if
返回-1
或在两个输出参数中传递结果:

create procedure dbo.TestProc
    ....
    @result int output,
    @errormessage varchar(256) output
as
set @result = -99
set @errormessage = null
....
if <errorcondition>
    begin
    set @result = -1
    set @errormessage = 'Condition failed'
    return @result
    end
创建过程dbo.TestProc
....
@结果int输出,
@errormessage varchar(256)输出
作为
设置@result=-99
设置@errormessage=null
....
如果
开始
设置@result=-1
设置@errormessage='条件失败'
返回@result
结束

我宁愿尽快返回,也不希望在过程结束时从同一点返回所有内容。多年前,我养成了组装的习惯。此外,我总是返回一个值:

RETURN 10
应用程序将在正数上显示致命错误,并在负值上显示用户警告消息

我们总是用错误消息的文本传回一个输出参数

例如:

IF ~error~
BEGIN
    --if it is possible to be within a transaction, so any error logging is not ROLLBACK later
    IF XACT_STATE()!=0
    BEGIN
        ROLLBACK
    END

    SET @OutputErrMsg='your message here!!'
    INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg)
    RETURN 10

END

从这个回答历史中可以看出,我遵循这个问题并接受了答案,然后继续“发明”一个与第二种方法基本相同的解决方案

咖啡因是我的主要能量来源,因为我花了太多时间编码,所以我的大部分时间都在半睡状态;因此,直到你正确地指出,我才意识到我的失礼

因此,作为记录,我更喜欢第二种方法:使用SP引发当前错误,然后在参数验证中使用TRY/CATCH


它减少了对所有IF/BEGIN/END块的需要,因此减少了行数,并将重点放回验证上。在阅读SP代码时,重要的是能够看到对参数执行的测试;在我看来,为了满足SQL解析器的要求,所有额外的语法漏洞都会成为障碍。

我总是使用参数@Is\u Success位作为输出。因此,如果我有一个错误,那么@Is_success=0。当父过程检查@Is_Success=0时,它将回滚其事务(与子事务一起),并从@error_message向客户端发送错误消息。

您使用IF(ISNULL(@fooString,)='')而不是IF(@fooString为null)有什么原因吗?@macleojw:他同时检查null和“”。。聪明:)第二个验证器的语法无效:“RAISEERROR”。应该只有一个“e”。有趣的是,在英语中它是正确的,因为“raise+error”有双“e”,但在MS SQL语言中没有。你应该使用SET NOCOUNT ON和SET NOCOUNT OFF。我想在另一个存储过程中使用存储过程,并希望尽可能少地进行错误检查,因此在外部存储过程中引发错误并捕获它似乎是最好的方法。有一天这个过程是从另一个位置调用的,我希望他们也记住捕获错误。我认为最好在发生错误时捕获所有错误,在本地处理它们,并返回适当的信息。Raiseerror是不可预测的(可能会继续执行!),并且不是每个客户端都以相同的方式处理它。perl客户端可能会死掉!你为什么要复制我的第二种方法,甚至重新发明我最初使用的改头换面SP?@VVS-哦,天哪,你说得对!这不是故意的,我通读了问题和答案。然后,由于缺乏睡眠,我很快就忘记了这里显示的所有选项,最初选择了if/begin/end结果,然后“发现”了这种方法——忘记了这是您尝试过的选项之一。道歉!嗯,只有当SP的返回值用于其他用途时,这才有意义。或者有没有一个很好的理由不只是执行“返回x”?您总是有两种可能的错误:SQL错误(例如XML解析)和逻辑错误(例如约束错误)。如果不区分它们,则会失去对应用程序行为的控制。若您在SQL之外记录错误,例如在文件系统中,那个么您的应用程序应该得到错误类型。