0

有没有办法查询传递给存储过程的参数并将它们作为 XML 返回,而无需创建参数字符串然后将其转换为 xml?我正在寻找通用的东西,它适用于大多数 SP,而不必每次都对其进行物理编码?

我有一堆存储过程可以访问和修改验证特定信息。在 SP 的末尾,我想将 SP 的名称以及用于调用 SP 的参数(以 xml 格式)插入到日志记录表中。我知道如何获取 SP 的名称,也知道如何获取 SP 的参数列表。我想要的是一种将其与传递的参数的实际值一起混入 XML 的方法。

我正在寻找可以做到这一点的东西,而无需手动编码每个参数:

DECLARE @L_Data varchar(1500)
SET @L_Data = '<parms>' + 
CASE WHEN @ParamRegStationID IS NULL THEN ''
ELSE ',@ParamRegStationID=''' + Convert(varchar, @ParamRegStationID) + '''' END +
CASE WHEN @ParamScheduleID IS NULL THEN '' 
ELSE ',@ParamScheduleID=''' + Convert(varchar, @ParamScheduleID) + '''' END +
CASE WHEN @ParamPatientID IS NULL THEN '' 
ELSE ',@ParamPatientID=''' + Convert(varchar, @ParamPatientID) + '''' END +
CASE WHEN @ParamHISPatientID IS NULL THEN '' 
ELSE ',@ParamHISPatientID=''' + @ParamHISPatientID + '''' END +
CASE WHEN @ParamEvent IS NULL THEN '' 
ELSE ',@ParamEvent=''' + @ParamEvent + '''' END +
'</parms>'

这是行不通的,也没有我希望的那么优雅。但是,这是一个示例,说明了我最终要达到的目标。它会创建临时表,但不会将参数作为列添加到其中,因此我可以稍后将其提取为 XML。

    ALTER PROC uspTest
    @ParamID as bigint=null,
    @ParamXYZ as varchar(255)=null
as

-- PROC Does whatever it is going to do ....

DECLARE @ProcName varchar(128), @ParmName varchar(128), @ParmType varchar(128), @ParmLen int, 
@ParmSQL varchar(1000)
select @ProcName=OBJECT_NAME(@@PROCID)
--select * from INFORMATION_SCHEMA.ROUTINES where ROUTINE_TYPE='PROCEDURE' and ROUTINE_NAME=@ProcName
DECLARE csrParms CURSOR
FOR 
select PARAMETER_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME=@ProcName and PARAMETER_MODE='IN' 
ORDER BY ORDINAL_POSITION
FOR READ ONLY

OPEN csrParms
FETCH NEXT FROM csrParms
INTO @ParmName, @ParmType, @ParmLen

CREATE TABLE #Parms(ID int identity(1,1), Created DateTime)
INSERT INTO #Parms select GETDATE()

WHILE @@FETCH_STATUS = 0
BEGIN
-- GET Parm value and format as xml attribute to save parm
SET @ParmSQL = 'ALTER TABLE #Parms add ' + @ParmName + ' varchar(' + CAST(ISNULL(@ParmLen, 128) as varchar(128)) + ') NULL '
print @ParmSQL
EXEC (@ParmSQL)
SET @ParmSQL = 'UPDATE #Parms SET ' + @ParmName + ' = ''????''' 
print @ParmSQL
--EXEC (@ParmSQL)
FETCH NEXT FROM csrParms
INTO @ParmName, @ParmType, @ParmLen
END

SET @ParmSQL = CAST((select * from #Parms FOR XML RAW) as varchar(1000))
select @ParmSQL

CLOSE csrParms
DEALLOCATE csrParms

这与我要找的很接近,我需要知道如何更换 ??? 虽然动态地使用参数的当前值。

    ALTER PROC uspTest
    @ParamID as bigint=null,
    @ParamXYZ as varchar(255)=null
as

-- PROC Does whatever it is going to do ....

DECLARE @ProcName varchar(128), @ParmName varchar(128), @ParmType varchar(128), @ParmLen int, 
@ParmSQL varchar(1000)
select @ProcName=OBJECT_NAME(@@PROCID)
--select * from INFORMATION_SCHEMA.ROUTINES where ROUTINE_TYPE='PROCEDURE' and ROUTINE_NAME=@ProcName
set @ParmSQL = 
'    CREATE TABLE #Parms(ID int identity(1,1), Created DateTime, ' + 
    STUFF((select (', ' + REPLACE(PARAMETER_NAME,'@','') + ' varchar(' + CAST(ISNULL(CHARACTER_MAXIMUM_LENGTH, 128) as varchar(128)) + ') NULL ')
     from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME='uspTest' and PARAMETER_MODE='IN' 
     order by ORDINAL_POSITION for XML path(''), type).value('.', 'varchar(max)'), 1, 2, '')
+ '); 
' + 'INSERT INTO #Parms (Created) select GETDATE(); ' + STUFF((select ('; 
UPDATE #Parms SET ' + REPLACE(PARAMETER_NAME,'@','') + ' = ''???''')
     from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME='uspTest' and PARAMETER_MODE='IN' 
     order by ORDINAL_POSITION for XML path(''), type).value('.', 'varchar(max)'), 1, 2, '')
+ '; 
select CAST((select * from #Parms FOR XML RAW) as varchar(1000));'

print @ParmSQL
EXEC (@ParmSQL)

当我执行proc时:

EXEC uspTest 1, 'test'

回报:

<row ID="1" Created="2012-04-20T09:44:43.700" ParamID="???" 参数XYZ="???"/>

打印出来:

CREATE TABLE #Parms(ID int identity(1,1), Created DateTime, ParamID varchar(128) NULL , ParamXYZ varchar(255) NULL ); 
INSERT INTO #Parms (Created) select GETDATE(); 
UPDATE #Parms SET ParamID = '???'; 
UPDATE #Parms SET ParamXYZ = '???'; 
select CAST((select * from #Parms FOR XML RAW) as varchar(1000));
4

1 回答 1

1

这是 SQL Server 2000 还是更高版本?如果是这样,您可以使用 FOR XML 子句:

DECLARE @p1 varchar(100) = 'blah'
    , @p2 int = 1
    , @p3 datetime2(7) = '2011-01-01 13:41'
    ;

SELECT @p1 StringParm
    , @p2 IntParm
    , @p3 DateParm
FOR XML RAW

返回:

<row StringParm="blah" IntParm="1" DateParm="2011-01-01T13:41:00"/>

编辑

啊,问题在于您需要将参数列表以及值(本地的)解析为动态 SQL(它们超出范围)。

我想您可以使用 INFORMATION_SCHEMA.PARAMETERS 动态列出参数和 dbcc_inputbuffers 以获取传递的实际值。就像是:

create procedure junk
    (   @int INT 
        , @string VARCHAR(20) 
        , @date DATE 
    )
AS 
BEGIN
    DECLARE @tmp TABLE
        (   EventType NVARCHAR(30)
            , PARMS INT
            , Info NVARCHAR(2000)
        );

    DECLARE @object NVARCHAR(200);

    INSERT INTO @tmp
    EXEC('DBCC INPUTBUFFER(@@SPID) WITH NO_INFOMSGS');

    SELECT INFO
        , 'Call' lType
    FROM @tmp
    UNION 
    SELECT STUFF(
        (   SELECT ', ' + parameter_name
            FROM INFORMATION_SCHEMA.PARAMETERS 
            WHERE SPECIFIC_NAME = OBJECT_NAME(@@procid)
            ORDER BY ORDINAL_POSITION
            FOR XML PATH('')
        )
        , 1
        , 2
        , ''
        )
        , 'Parms';
END

现在这样:

exec dbo.junk  @int = 3, @string = 'hoo', @date = '2/2/2002';

回报:

exec dbo.junk  @int = 3, @string = 'hoo', @date = '2/2/2002';   Call
@int, @string, @date                                                 Parms

这应该让你一路走好。棘手的一点是 DBCC_INPUTBUFFERS 返回 EXACT 调用字符串。因此,您需要编写代码来解析调用以将输入行与参数列表匹配。如果你走那条路,你可能需要一个存储函数来进行解析。它可能会采用类似于上述返回值的调用字符串和参数列表,匹配它们,然后使用 FOR XML 子句返回您想要的格式。

您还可以在绑定到参数列表的游标中解析调用字符串。然后您将按顺序提取参数并查找逗号和@。如果您没有考虑到这一点,您仍然可能会遇到包含这些字符的参数值的问题。

恕我直言,与几乎可以从函数头复制/粘贴的简单选择相比,将其平方似乎需要做很多工作。当然,如果您谈论的是大量程序,那么它可能是值得的。不管怎样,祝你好运,感谢你提出发人深省的问题。

于 2012-04-19T23:10:16.477 回答