SQL动态创建参数的xml

时间:2012-04-19 14:31:00

标签: sql xml parameters

有没有办法查询传递给存储过程的参数并将它们作为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 as:

EXEC uspTest 1, 'test'

返回:

  

&lt; row ID =“1”Created =“2012-04-20T09:44:43.700”ParamID =“???” ParamXYZ = “???”/&GT;

打印出来:

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));

1 个答案:

答案 0 :(得分:0)

这是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子句返回所需的格式。

您还可以解析绑定到参数列表的游标中的调用字符串。然后你按顺序拉出参数并查找逗号和@。如果不考虑这些字符,您仍可能遇到包含这些字符的参数值的问题。

恕我直言,相比于一个几乎可以从函数头中复制/粘贴的简单选择,这种平方似乎要做很多工作。当然,如果你在谈论大量的程序,那么它可能是值得的。无论哪种方式,祝你好运,并感谢一个发人深思的问题。