我的目标是从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
首先会出现错误“架构已存在”,然后另一个错误“无法创建架构,请参阅上一个错误”,但现在包含关键信息的第一个关键错误已被“吃掉”。
如何显示所有错误消息?
答案 0 :(得分:1)
Ivan关于ERROR_MESSAGE
以及TRY-CATCH
如何删除查询的强健性质是正确的,但是,这只发生在TRY块中消息的SEVERITY
大于10时。所以诀窍是将严重性设置为11。
如果运行RAISERROR,则会将错误返回给调用者:
- 超出任何TRY区块的范围。
- TRY块中的严重性为10或更低。
- 严重性为20或更高,终止数据库 连接。
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.
有很多方法可以检查动态SQL
,DDL
和DML
的有效性,包括有用的功能,例如OBJECT_ID
,OBJECT_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
它确实给出了选择的输出:
和所有消息:
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