error_line() 返回错误在存储过程源代码中的物理行号,仅在catch块中有效,动态sql或远程操作出错时返回执行语句行号而非内部行号。

ERROR_LINE() 返回的行号是存储过程定义里的行号,不是调用时的行号
SQL Server 的 ERROR_LINE() 在 CATCH 块中返回的是错误实际发生的**存储过程源代码中的物理行号**,不是你执行语句时所在的位置(比如调用它的外部脚本行号)。这个行号从存储过程 CREATE PROCEDURE 语句之后的第一行开始计算,包括空行和注释行。
常见误解是以为它能定位到“哪条 EXEC 调用出错了”,其实不能——它只反映错误在当前存储过程体内的位置。
- 如果存储过程是用
ALTER PROCEDURE修改的,ERROR_LINE()基于最新版本的源码行号 - 动态 SQL(
EXEC(@sql)或sp_executesql)内部出错时,ERROR_LINE()返回的是EXEC那一行的号,不是动态语句里的行号 - 触发器中也能用
ERROR_LINE(),但返回的是触发器体内的行号,不是引发触发的 DML 语句位置
必须搭配 TRY…CATCH 才能获取有效值
ERROR_LINE() 只有在 CATCH 块中调用才有意义;在 TRY 块或存储过程其他地方直接调用,会返回 NULL。
示例片段:
CREATE PROCEDURE usp_DemoError
AS
BEGIN
BEGIN TRY
SELECT 1/0; -- 这行假设是第 5 行
END TRY
BEGIN CATCH
SELECT
ERROR_LINE() AS ErrorLine, -- 返回 5
ERROR_MESSAGE() AS Message,
ERROR_NUMBER() AS Number;
END CATCH
END
- 不写
TRY…CATCH,ERROR_LINE()拿不到值 -
SET XACT_ABORT ON不影响ERROR_LINE()的可用性,但它会让某些错误直接终止批处理,跳过CATCH,导致无法捕获 - 嵌套存储过程中出错,
ERROR_LINE()返回的是**最内层发生错误的那个过程**的行号,不是调用链上的上层过程
行号可能因格式变化而偏移,别硬编码依赖它做逻辑分支
开发中有人试图用 IF ERROR_LINE() = 42 BEGIN ... END 来区分错误位置并走不同恢复逻辑——这很危险。只要有人在前面增删空行、注释或调整语句顺序,行号就变了,逻辑就断了。
更可靠的做法是结合 ERROR_NUMBER() 和业务上下文判断错误类型,而不是死盯行号。
- 调试阶段可以打印
ERROR_LINE()辅助定位,但生产环境日志里不应仅靠它归因 - SSMS 中右键“修改”存储过程后保存,行号重算;用源码管理工具部署时,若格式化脚本,行号也会变
- 若真需要精确标记,改用自定义错误号(
RAISERRORwith state 或THROW自定义 message)配合注释说明位置
动态 SQL 错误无法通过 ERROR_LINE() 定位到动态内容内部
这是最容易踩坑的地方:你在动态拼的 SQL 里除零、类型转换失败,ERROR_LINE() 返回的只是执行 EXEC(@sql) 那一行的号,不是 @sql 字符串里第几行出问题。
例如:
DECLARE @sql NVARCHAR(MAX) = N' SELECT 1/0; -- 这行在动态字符串里是第 1 行,但 ERROR_LINE() 不认它 '; EXEC(@sql); -- 假设这行是存储过程第 12 行 → ERROR_LINE() 返回 12
- 想定位动态 SQL 内部错误,得在动态字符串里加
TRY…CATCH并用RAISERROR把上下文带出来 - 或者改用
sp_executesql+ 外层TRY…CATCH,再配合ERROR_PROCEDURE()确认是当前过程,避免误判为系统过程报错 -
ERROR_LINE()对OPENQUERY、链接服务器查询等远程操作也一样——只返回本地执行语句的行号
复杂点在于:行号是编译时快照,不是运行时映射;而人眼看到的“第几行”常受编辑器设置(如自动换行、折叠)干扰。上线前用 sys.dm_exec_sql_text 查一下实际缓存的定义,比靠记忆或截图更准。











