我正在尝试在 catch 块中记录 SP 的参数,我已经为参数创建了动态字符串。但是,当它插入表时,它会插入字符串而不是参数的值。下面是我的代码:
DECLARE @ErrorMessage VARCHAR(4000),
@ErrorSeverity INT,
@ErrorState INT,
@ErrorProcedure VARCHAR(200),
@ErrorLine INT,
@ErrorNumber INT,
@ErrorParams VARCHAR(MAX)='';
-- Get the error information
SET @ErrorMessage = ERROR_MESSAGE();
SET @ErrorSeverity = ERROR_SEVERITY();
SET @ErrorState = ERROR_STATE();
SET @ErrorProcedure = ERROR_PROCEDURE();
SET @ErrorLine = ERROR_LINE();
SET @ErrorNumber = ERROR_NUMBER();
DECLARE @SPParameter AS TABLE (Id INT IDENTITY PRIMARY KEY, ParaName VARCHAR(100))
INSERT INTO @SPParameter (ParaName)
SELECT [name]
FROM sys.parameters
WHERE OBJECT_ID = OBJECT_ID(@ErrorProcedure)
DECLARE @minId INT, @maxId INT
SELECT @minId = MIN(Id),@maxId = MAX(Id) FROM @SPParameter
SET @ErrorParams = ''''
WHILE @minId <= @maxId
BEGIN
SELECT @ErrorParams = @ErrorParams + ''+ParaName+' = ''+ISNULL(CAST('+CAST(ParaName AS varchar)+' AS VARCHAR),'''')+'', '
FROM @SPParameter WHERE Id=@minId
SET @minId = @minId + 1
END
SET @ErrorParams = LEFT(@ErrorParams, LEN(@ErrorParams) - 2)
SET @ErrorParams = @ErrorParams + ''''''
INSERT INTO ErrorLog (ErrorMessage, ErrorSeverity, ErrorState, ErrorProcedure, ErrorLine, ErrorNumber, ErrorParams)
VALUES (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorNumber, @ErrorParams);
它正在插入我动态创建的字符串。下面是插入到我的日志表中的字符串:
'@Param1 = '+ISNULL(CAST(@Param1 AS VARCHAR),'') +
', @Param2 = '+ISNULL(CAST(@Param2 AS VARCHAR),'') +
', @Param3 = '+ISNULL(CAST(@Param3 AS VARCHAR),'') +
', @Param4 = '+ISNULL(CAST(@Param4 AS VARCHAR),''
免责声明:我不一定建议将此方法用于一般用途,但我将其发布为原始海报在上述问题中试图完成的替代方案。
为了实现您的目标,您也许可以使用如下初始代码片段来编写存储过程:
DECLARE @ErrorParams VARCHAR(MAX);
SET @ErrorParams = 'Not yet customized';
然后,您可以针对所有已定义的存储过程运行一个进程,以将上述代码片段替换为以下内容:
SET @ErrorParams = CONCAT('@Parm1=', @Parm1, ', ', '@Parm2=', @Parm2, ...);
以下逻辑可以使用游标循环来完成此操作,该循环检索每个存储过程定义,找到
SET @ErrorParams = ...;
语句,将其替换为实际的存储过程参数,然后使用 ALTER PROCEDURE
语句重新应用该定义。
这可能还没有100%准备好迎接黄金时段,但这是一个开始。我建议您在应用此过程之前编写脚本并安全地存储原始存储过程,以防您放错了“;”或者出现其他问题。
DECLARE CSR CURSOR FOR
SELECT P.object_id, P.name, D.Definition
FROM sys.procedures P
CROSS APPLY (SELECT OBJECT_DEFINITION(P.object_id) AS Definition) D
WHERE P.name LIKE 'Proc%'
AND D.Definition LIKE '%SET @ErrorParams =%;%'
OPEN CSR
DECLARE @ObjectId INT
DECLARE @Name SYSNAME
DECLARE @Definition NVARCHAR(MAX)
FETCH NEXT FROM CSR INTO @ObjectId, @Name, @Definition
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @StartPos INT = CHARINDEX('SET @ErrorParams =', @Definition)
DECLARE @EndPos INT = CHARINDEX(';', @Definition, @StartPos)
DECLARE @Length INT = @EndPos - @StartPos + 1
DECLARE @ErrorParamsList NVARCHAR(MAX) = (
-- '@Parm1=', @Parm1, ', ', '@Parm2=', @Parm2, ', ', '@Parm3=', @Parm3, ...'
SELECT STRING_AGG(I.Item, ', '', '', ') WITHIN GROUP(ORDER BY PRM.parameter_id)
FROM sys.parameters PRM
JOIN sys.types T
ON T.user_type_id = PRM.user_type_id
CROSS APPLY (
SELECT
CASE
WHEN T.name = 'xml' -- Explicit conversion needed
THEN 'CONVERT(NVARCHAR(MAX), ' + PRM.name + ')'
WHEN T.name in ('binary', 'varbinary') -- Custom conversion
THEN 'CONVERT(NVARCHAR(MAX), ' + PRM.name + ', 2)'
WHEN T.name in ('datetime', 'datetime2') -- Custom conversion
THEN 'CONVERT(NVARCHAR(MAX), ' + PRM.name + ', 21)'
-- ... Handle other custom or unsupported cases
ELSE PRM.name -- Default conversion handled by CONCAT()
END AS Expression
) E
CROSS APPLY (
-- '@ParmN=, @ParmN'
-- TODO: Special handling for certain parameter types (binary, table, geo?)
SELECT QUOTENAME(PRM.name + '=', '''') + ', ' + E.Expression AS Item
) I
WHERE PRM.object_id = @ObjectId
)
DECLARE @ErrorParamsStatement NVARCHAR(MAX) =
'SET @ErrorParams = CONCAT(' + @ErrorParamsList + ');'
DECLARE @UpdatedDefinition NVARCHAR(MAX)
= STUFF(@Definition, @StartPos, @Length, @ErrorParamsStatement)
-- The original definition might be a CREATE PROC (or PROCEDURE)
-- Change to an ALTER. Only one space is allowed afterthe CREATE.
-- *** This is *not* entirely safe, if the code contains other "CREATE PROC" content.
SET @UpdatedDefinition = REPLACE(@UpdatedDefinition, 'CREATE PROC', 'ALTER PROC')
PRINT ''
PRINT 'Procedure Name: ' + OBJECT_NAME(@ObjectId)
PRINT 'ErrorParamsList: ' + @ErrorParamsList
PRINT 'ErrorParamsStatement: ' + @ErrorParamsStatement
PRINT 'Updated Definition:' + CHAR(10) + @UpdatedDefinition
EXEC (@UpdatedDefinition)
FETCH NEXT FROM CSR INTO @ObjectId, @Name, @Definition
END
CLOSE CSR
DEALLOCATE CSR
尽管语句终止符 (;) 在 SQL Server 中通常是可选的,但上述逻辑需要它的存在。
对于某些参数类型,需要显式或自定义转换。以上仅涵盖了几种,但可能还有其他需要定制的情况。如果您使用表参数或用户定义类型,这些可能需要特殊处理。
Null 值显示为空值。字符串、XML 和其他通常可能在代码中引用的值在上述实现中未引用。
包含换行符的字符串可能会在日志中跨多行显示。您可能想阅读“日志注入”并确定这是否是一个问题。
请参阅 this db<>fiddle 进行演示。