如何在单个Query中返回多个错误

时间:2016-06-30 02:44:12

标签: sql sql-server

我的目标是从SQL查询中捕获错误消息,记录或打印然后传递它而不是让它产生真正的错误。但我发现从检查查询中捕获多个错误是不可能的;只捕获最后一个错误:

DECLARE @ErrorMessage varchar(1000)
BEGIN TRY
    EXEC('SELECT AA,BB FROM TABLE')--neither column AA nor BB exists
END TRY
BEGIN CATCH
SET @ErrorMessage  = 'ERRORMESSAGE: ' + Error_Message()
PRINT @ErrorMessage
END CATCH

查询只会提供BB列无法找到的反馈,但无法显示AA列也不存在。

或者另一个例子,将此查询放在TRY

EXEC('CREATE SCHEMA abc AUTHORIZATION [dbo]') --schema abc already exists 

首先会出现错误“架构已存在”,然后另一个错误“无法创建架构,请参阅上一个错误”,但现在包含关键信息的第一个关键错误已被“吃掉”。

如何显示所有错误消息?

3 个答案:

答案 0 :(得分:1)

  • 你仍然可以使用RAIDERROR内部试验块

Ivan关于ERROR_MESSAGE以及TRY-CATCH如何删除查询的强健性质是正确的,但是,这只发生在TRY块中消息的SEVERITY大于10时。所以诀窍是将严重性设置为11。

  

如果运行RAISERROR,则会将错误返回给调用者:

     
      
  • 超出任何TRY区块的范围。
  •   
  • TRY块中的严重性为10或更低。
  •   
  • 严重性为20或更高,终止数据库   连接。
  •   
     

MSDN - RAISERROR

RAISERROR可用作PRINT的替代,并允许自定义消息。 此外,您可以将STATE设置为不同的数字,以跟踪代码中类似但不同的错误。

由于致命错误将是您的祸根,我建议您在运行它们之前测试查询和DDL命令。例如,您可以尝试使用ad-hoc消息,而不是盲目地尝试EXEC('CREATE SCHEMA abc AUTHORIZATION [dbo]')

DECLARE @SCHEMA NVARCHAR(10) 
DECLARE @Message NVARCHAR(255)
SET @SCHEMA = N'abc'
SET @Message = N'The Schema ' + @SCHEMA + ' already exists.'

IF SCHEMA_ID(@SCHEMA) IS NOT NULL
EXEC('CREATE SCHEMA abc AUTHORIZATION [dbo]')
ELSE RAISERROR(@Message, 10, 1)
--result: The Schema abc already exists.

有很多方法可以检查动态SQLDDLDML的有效性,包括有用的功能,例如OBJECT_IDOBJECT_NAME,{{ 1}}等安全测试的地方,然后针对每个错误运行相应的DATABASE_ID消息。

答案 1 :(得分:0)

如果可能,请删除TRY-CATCH - 将脚本语句划分为多个单独的批次GO

TRY-CATCH对第一个异常做出反应并中断TRY-block的执行:

  

如果TRY块中发生错误,则控制权将传递给另一个   包含在CATCH块中的语句组。

https://msdn.microsoft.com/en-us/library/ms175976.aspx

因此,TRY-CATCH的行为与您的意图相反。

GO设置批次的结束。许多错误甚至都没有破坏批处理,因为它们有low severity,因此在某些情况下甚至不需要将脚本拆分成多个批次。

这里的示例是用于测试的示例虚拟脚本或用于生成许多错误的某些实用程序(当然不用于生产):

create proc SomeProc as
begin
  exec('select uknown from non_existent')
end
GO
drop table #test1
drop table #test2
GO
drop table #test3
GO
create table #test1 (id int primary key)

insert into #test1(id)
exec SomeProc

insert into #test
values (1)

insert into #test1
values (1)
GO
insert into #test1
values (11)

insert into #test1
values (11)

insert into #test
values (22)
GO
select * from #test1
GO
drop table #test
GO
drop table #test
drop proc SomeProc
select object_id('SomeProc', 'P')
GO

它确实给出了选择的输出:

enter image description here

和所有消息:

  

Msg 3701,Level 11,State 5,Line 7无法删除表'#test2',   因为它不存在或您没有权限。

     

Msg 3701,   等级11,状态5,行9不能掉落'#test3'表,因为它   不存在或您没有权限。

     

Msg 208,Level 16,State   1,第11行无效的对象名称'non_existent'。

     

(0行(s)受影响)

     

消息208,级别16,状态0,行16无效对象   名字'#test'。

     

(1行受影响)

     

Ms 2627,Level 14,State 1,Line 25违反   PRIMARY KEY约束'PK __#test1____3213E83FF35979C1'。无法插入   对象'dbo。#test1'中的重复键。重复键值为(11)。   该语句已终止。

     

Msg 208,Level 16,State 0,Line 28   无效的对象名称'#test'。

     

(1行受影响)

     

Msg 3701,Level 11,State 5,Line 33不能掉线   表'#test',因为它不存在或你没有   允许。

     

Msg 3701,Level 11,State 5,Line 35不能放弃桌子   '#test',因为它不存在或您没有权限。

“我的目标是从SQL查询中捕获错误消息,记录或打印然后传递它而不是让它产生真正的错误。” - 如果“print”没问题则只需删除{{ 1}}。

答案 2 :(得分:0)

通过sqlcmd运行脚本并将错误重定向到文件: How to get SQLCMD to output errors and warnings only

  

sqlcmd -i Script.sql -E -r1 1> NUL