动态检索参数名称& T-SQL存储过程中的当前值

时间:2015-06-02 12:21:45

标签: sql-server tsql stored-procedures

我有数百个用于商业智能的模板化ETL存储过程。他们将其运营活动记录到审计表中。缺少的一件事是记录传递给它们的参数信息。问题是从一个SP到另一个SP的参数并不总是相同的。我正在寻找一个标准的代码片段,我可以坚持到可以遍历proc的所有参数并检索传入的当前值的过程。我计划将它们一起拼接在一个字符串中以便也记录到表中。有什么想法吗?

提前感谢您的任何指示! - 蒂姆

3 个答案:

答案 0 :(得分:2)

我正在寻找一个标准的代码片段,我可以直接进入可以遍历proc的所有参数并检索传入的当前值的过程 -

您可以使用以下查询获取sp传入的所有值

示例:
我有以下存储过程,它给我销售详细信息(仅限演示)

alter  proc dbo.getsales
(
@salesid int
)
as
begin
select 
* from sales where cust_id=@salesid
end

我在下面打电话给我的sp ..

exec  dbo.getsales 4

现在,如果我想获得价值,我可以使用以下查询

select top 10* from sys.dm_exec_cached_plans cp
cross apply
sys.dm_exec_text_query_plan(cp.plan_handle,default,default)
where objtype='proc'

它在下面显示了编译时间值 enter image description here

据说,有很多事情需要考虑。我们可以使用xml方法来获取这个值

现在会发生什么,如果我再次为值2运行相同的存储过程。

<ColumnReference Column="@salesid" ParameterCompiledValue="(4)" ParameterRuntimeValue="(2)" />

这里的一个重要问题是,当我选择从ssms显示的执行计划时,会显示上述值。

但是缓存中的值是什么,让我们再次使用上面的计划缓存查询来查看它

<ColumnReference Column="@salesid" ParameterCompiledValue="(4)"/>

它仍然显示编译值,加上usecounts列为5 - `这意味着该计划已被使用了5次,并且在计划最初编译时传递的参数是4.这也意味着,运行时值不是存储在缓存计划详细信息中..

总而言之,您可以将运行时值传递给存储过程

  • 1.编制陈述时传递的价值(
    您可以在一段时间内开始收集此信息并将其记录在存储过程中,我认为随着服务器重新启动,计划重新编译可以获得新的参数值集 )
  • 2.与DEV团队保持联系也是一个很好的方法,因为他们可以为您提供可以传递的参数的完整列表,如果这样的话    练习是立体声

答案 1 :(得分:0)

您不必动态检索存储过程中 的名称和参数,因为一旦创建或更改了存储过程,它就无法更改其参数,直到它被改变或重新创造。

相反,您可能在存储的proc static 中包含参数列表,但是为了不手动枚举params,您可以通过DDL触发器动态生成它。

定义一些注释标记,用于标记位置,参数应在存储过程中列出,并在适当的时候将它们添加到存储过程的主体中。触发器应该在标记之间找到标记和alter proc插入参数名称的静态列表及其值。示例如下。

<强> DDL-触发

create trigger StoredProc_ListParams on database
for CREATE_PROCEDURE, ALTER_PROCEDURE
as
begin
    set nocount on;

    if @@nestlevel > 1
        return;

    declare @evt xml, @sch sysname, @obj sysname, @text nvarchar(max);

    set @evt = eventdata();
    set @text = @evt.value('(/EVENT_INSTANCE/TSQLCommand/CommandText/text())[1]', 'nvarchar(max)');

    if @text is NULL -- skip encrypted
        return;

    set @sch = @evt.value('(/EVENT_INSTANCE/SchemaName/text())[1]', 'sysname');
    set @obj = @evt.value('(/EVENT_INSTANCE/ObjectNa1me/text())[1]', 'sysname');

    declare @listParams nvarchar(max);

    set @listParams = '
    select name, value
    from (values ' + stuff(
        (select ',
        ' + '(' + cast(p.parameter_id as varchar(10)) + ', ''' + p.name + '''' +
        ', cast(' + p.name + ' as sql_variant))'
    from sys.parameters p
        join sys.objects o on o.object_id = p.object_id and o.type = 'P'
        join sys.schemas s on s.schema_id = o.schema_id
    where s.name = @sch and o.name = @obj
    order by p.parameter_id
    for xml path(''), type).value('text()[1]', 'nvarchar(max)'), 1, 1, '') + '
        ) p(num, name, value)
    order by num';

    declare @startMarker nvarchar(100), @endMarker nvarchar(100);
    set @startMarker = '--%%LIST_PARAMS_START%%';
    set @endMarker = '--%%LIST_PARAMS_END%%';

    if left(@text, 6) = 'create'
        set @text = stuff(@text, 1, 6, 'alter');

    declare @ixStart int, @ixEnd int;
    set @ixStart = nullif(charindex(@startMarker, @text), 0) + len(@startMarker);
    set @ixEnd = nullif(charindex(@endMarker, @text), 0);

    if @ixStart is NULL or @ixEnd is NULL
        return;

    set @text = stuff(@text, @ixStart, @ixEnd - @ixStart, @listParams + char(13) + char(10));

    if @text is NULL
        return;

    exec(@text);
end

测试的存储过程的脚本:

create procedure dbo.TestProc
(
    @id int,
    @name varchar(20),
    @someFlag bit,
    @someDate datetime
)
as
begin
    set nocount on;

--%%LIST_PARAMS_START%%
    -- list params for me here, please
--%%LIST_PARAMS_END%%

end

以下是执行上述创建脚本后,存储过程实际在数据库中的显示方式:

alter procedure [dbo].[Test]
(
    @id int,
    @name varchar(20),
    @someFlag bit,
    @someDate datetime
)
as
begin
    set nocount on;

--%%LIST_PARAMS_START%%
    select name, value
    from (values 
        (1, '@id', cast(@id as sql_variant)),
        (2, '@name', cast(@name as sql_variant)),
        (3, '@someFlag', cast(@someFlag as sql_variant)),
        (4, '@someDate', cast(@someDate as sql_variant))
        ) p(num, name, value)
    order by num
--%%LIST_PARAMS_END%%

end

这种方法的一个限制是它不适用于加密的存储过程。如果您希望处理表类型的参数,还需要进行一些调整。

答案 2 :(得分:0)

从SQL Server 2014开始,我们有了sys.dm_exec_input_buffer,它是一个表值函数,其输出列event_info给出了完整的执行语句(包括参数)。

您可以阅读完整的执行语句,然后从中解析出参数值。

例如:

这些行包含在正在执行的存储过程中

-- get the full execution statement
declare @statement nvarchar(max)
select  @statement = event_info
from    sys.dm_exec_input_buffer(@@spid, current_request_id())

-- parse params from the statement
declare @proc_name varchar(128) = object_name(@@procid)
declare @param_idx int = charindex(@proc_name, @statement) + len(@proc_name)
declare @param_len int = len(@statement) - @param_idx 
declare @params nvarchar(max) = right(@statement, @param_len)

print @params

-- or log them...
exec log_error sysdatetime(), @proc_name, @params, error_line(), error_message()

我用它来记录捕获块中的错误。