4

第1部分

DECLARE @A INT
DECLARE @B NVARCHAR(20)

SET @A=123
SET @B='@A'

第2部分

DECLARE @SQL NVARCHAR(MAX)
SET @SQL='SELECT ' + @B

EXEC SP_EXECUTESQL @SQL
--Should return 123

对于此任务,在非动态 SQL 中直接引用 @A 是不可接受的。

第 2 部分中的上述内容通常是我想要做的。我知道该变量超出了范围,它不会像上面那样工作。我如何使用@B 来获取@A 的值?

更新 20190322:我实际上忘记了这个问题,并在 C# 端而不是数据库上实现了日志记录,但如果这可能的话,我再次感到好奇。同样,这需要是通用的,因为我想把它放在任何存储过程的顶部,我不想为每个 sproc 定制它;我在光标获取参数值时遇到问题。这是一个工作示例:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO    

ALTER PROCEDURE [dbo].[LoggingTest]
    @DateBegin datetime,
    @DateEnd datetime,
    @Person varchar(8000),
    @Unit varchar(8000)
AS
BEGIN
    --BEGIN LOGGING CODE
    DECLARE @String NVARCHAR(MAX)=''
    DECLARE @Parameter_name nvarchar(2000), @type nvarchar(50), @length SMALLINT, @Prec SMALLINT, @Scale SMALLINT, @Param_order SMALLINT, @Collation nvarchar(2000);  
    DECLARE param_cursor CURSOR FOR   
        SELECT
           'Parameter_name' = name,  
           'Type'   = type_name(user_type_id),  
           'Length'   = max_length,  
           'Prec'   = case when type_name(system_type_id) = 'uniqueidentifier' 
                      then precision  
                      else OdbcPrec(system_type_id, max_length, precision) end,  
           'Scale'   = OdbcScale(system_type_id, scale),  
           'Param_order'  = parameter_id,  
           'Collation'   = convert(sysname, 
                           case when system_type_id in (35, 99, 167, 175, 231, 239)  
                           then ServerProperty('collation') end)  
        from sys.parameters
        where object_id = object_id(OBJECT_NAME(@@PROCID))

    OPEN param_cursor  

    FETCH NEXT FROM param_cursor   
    INTO @Parameter_name,@type,@length,@Prec,@Scale,@Param_order,@Collation

    WHILE @@FETCH_STATUS = 0  
    BEGIN  
        SET @String=@String + @Parameter_name + '==' --+ SELECT @Parameter_name --This is part I can't think of a way to do; need to record/capture the value

        SET @String=@String + CHAR(13) + CHAR(10)
        FETCH NEXT FROM param_cursor   
        INTO @Parameter_name, @type,@length,@Prec,@Scale,@Param_order,@Collation
    END   
    CLOSE param_cursor;  
    DEALLOCATE param_cursor;

    --INSERT DATA INTO LOG TABLE HERE
    SELECT OBJECT_SCHEMA_NAME(@@PROCID) + '.' + OBJECT_NAME(@@PROCID) AS [ProcedureName],@String AS [Data],GETDATE() AS [LogTime]
    --END LOGGING CODE    

    /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        DO BUSINESS STUFF HERE!
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
    /*
    BLAH
    BLAH
    BLAH

        DECLARE @Now DATETIME=GETDATE()
        EXEC [dbo].[LoggingTest] @Now,@Now,'Person Value','Unit Value'
    */
END
GO
4

4 回答 4

5

我不明白您要问的全部内容,但是您可以在以下位置定义变量sp_executesql

EXEC SP_EXECUTESQL @SQL, N'@A INT', @A = @A
于 2013-09-03T19:17:29.110 回答
1

虽然你不必这样做,但仍然是这样:

SET @B=@A

您可以直接分配@A的值:

 DECLARE @SQL NVARCHAR(MAX)
 SET @SQL='SELECT ' + Convert(varchar(50),@A)
于 2013-09-03T19:16:15.277 回答
1

找了一圈发现可以

  1. dm_exec_requests拥有来自&的运行过程代码dm_exec_sql_text

  2. dm_exec_input_buffer. 我应该注意,我对此不是很精通,而且我想我在某处读到它不适用于在 SSMS 之外完成的查询......

  3. 正如您的代码中已经看到的那样,参数可以在sys.parameters

因此,我实现了一种方法来做到这一点。主要算法步骤为:

  1. 创建一个具有相同参数和默认值的虚拟 proc。将其主体更改为单个SELECT. 通过在原始过程中使用更高一级的参数的动态 xml 连接,我们可以为虚拟过程提供一个VALUES表,其中包括参数名称(带引号)和它们的值(不带引号)。
  2. 使用原始 spc 中使用的相同调用参数执行虚拟 proc。INSERT结果集到EXEC临时表中。之后删除虚拟过程。

现在我们有一个包含参数名称和值的临时表。

大多数新代码都在您的代码开始之前,除了您在光标中需要的实际值。此外,为了避免激烈的解析,我使用了一个(便宜的)技巧,--postBeginParserMarker在之后添加评论,BEGIN以便我知道 proc 从哪里开始......

alter PROCEDURE [dbo].[LoggingTest]
    @DateBegin datetime,
    @DateEnd datetime,
    @Person varchar(8000),
    @Unit varchar(8000)='someunithere'
AS
BEGIN --postBeginParserMarker
    declare @SPID int= @@SPID;
    declare @request_id int;
    declare @dummyspc nvarchar(max)
    select @dummyspc = 'spc_dummy_'+ convert(nvarchar,max(object_id)+1) from sys.procedures -- just a way to ensure no conflicts between simultaneous runs of this very proc
    --select @dummyspc

    declare @thisprocname sysname
    select @thisprocname=o.name,@request_id=request_id
    from sys.dm_exec_requests r
    cross apply sys.dm_exec_sql_text(r.sql_handle) t
    inner join sys.objects o on t.objectid=o.object_id
    where r.session_id=@SPID
    --select @thisprocname

    declare @newproc nvarchar(max)
    SELECT @newproc=substring(st.text,1,CHARINDEX ('--postBeginParserMarker' , st.text)-1 )
    FROM sys.dm_exec_requests r
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
    where r.session_id=@SPID
    set @newproc=replace(@newproc,@thisprocname,@dummyspc)

    SELECT @newproc+=
         char(13)+char(10)
        +'select * from ( values'
        +STUFF
        (
            (    
                SELECT char(13)+char(10)+char(9)+',('+convert(nvarchar,parameter_id)+','''+name+''',convert(nvarchar,'+name+'))'
                from sys.parameters 
                where object_id=object_id(@thisprocname)
                FOR XML PATH('') , TYPE
            ).value('.','nvarchar(max)')
            , 4, 1, ' ' 
        )
        +char(13)+char(10)+')t(parameter_id,parameter_name,parameter_value)'
        +char(13)+char(10)+'END'
    --select @newproc

    declare @newproccall nvarchar(max)
    select @newproccall=event_info from sys.dm_exec_input_buffer ( @SPID ,@request_id)
    set @newproccall=replace(@newproccall,@thisprocname,@dummyspc)
    --select @newproccall

    exec(@newproc)

    if object_id('tempdb..#paramtbl') is not null drop table #paramtbl
    create table #paramtbl (parameter_id int,parameter_name nvarchar(max),parameter_value nvarchar(max))
    insert #paramtbl(parameter_id,parameter_name,parameter_value)
    exec(@newproccall)

    -- select * from #paramtbl  <-- Now this has all you need

    exec('drop procedure '+@dummyspc)

    --BEGIN LOGGING CODE
    DECLARE @String NVARCHAR(MAX)=''
    DECLARE @Parameter_name nvarchar(2000), @type nvarchar(50), @length SMALLINT, @Prec SMALLINT, @Scale SMALLINT, @Param_order SMALLINT, @Collation nvarchar(2000);  

    --select @Unit as val
    --drop table ##a 
    --select @@SPID


    DECLARE param_cursor CURSOR FOR   
        SELECT
           'Parameter_name' = name,  
           'Type'   = type_name(user_type_id),  
           'Length'   = max_length,  
           'Prec'   = case when type_name(system_type_id) = 'uniqueidentifier' 
                      then precision  
                      else OdbcPrec(system_type_id, max_length, precision) end,  
           'Scale'   = OdbcScale(system_type_id, scale),  
           'Param_order'  = parameter_id,  
           'Collation'   = convert(sysname, 
                           case when system_type_id in (35, 99, 167, 175, 231, 239)  
                           then ServerProperty('collation') end)  
        from sys.parameters
        where object_id = object_id('LoggingTest')

    OPEN param_cursor  

    FETCH NEXT FROM param_cursor   
    INTO @Parameter_name,@type,@length,@Prec,@Scale,@Param_order,@Collation

    WHILE @@FETCH_STATUS = 0  
    BEGIN  
        SET @String=@String + @Parameter_name + '==' + (SELECT isnull(parameter_value,'<NULL>') from #paramtbl where parameter_id=@Param_order)

        SET @String=@String + CHAR(13) + CHAR(10)
        FETCH NEXT FROM param_cursor   
        INTO @Parameter_name, @type,@length,@Prec,@Scale,@Param_order,@Collation
    END   
    CLOSE param_cursor;  
    DEALLOCATE param_cursor;

    --INSERT DATA INTO LOG TABLE HERE
    SELECT OBJECT_SCHEMA_NAME(@@PROCID) + '.' + OBJECT_NAME(@@PROCID) AS [ProcedureName],@String AS [Data],GETDATE() AS [LogTime]
    --END LOGGING CODE    

    /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        DO BUSINESS STUFF HERE!
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
    /*
    BLAH
    BLAH
    BLAH

        DECLARE @Now DATETIME=GETDATE()
        EXEC [dbo].[LoggingTest] @Now,@Now,'Person Value','Unit Value'
    */
END
GO
于 2019-03-22T23:32:35.580 回答
1

从 SQL Server 2014 SP2 开始,有一个sys.dm_exec_input_buffer动态管理视图。

在以前的版本中,有DBCC INPUTBUFFER.

它返回一个表,其中包含一个列event_info,其中包含“给定 spid 的输入缓冲区中的语句文本”。

因此,您可以在存储过程的开头添加一个简单的查询,如下所示:

INSERT INTO LoggingTable(event_info, event_type, parameters)
SELECT
    InBuf.event_info
    ,InBuf.event_type
    ,InBuf.parameters
FROM
    sys.dm_exec_requests AS Req
    INNER JOIN sys.dm_exec_sessions AS Ses ON Ses.session_id = Req.session_id
    CROSS APPLY sys.dm_exec_input_buffer(Req.session_id, Req.request_id) AS InBuf
WHERE
    Ses.session_id > 50 
    AND Ses.is_user_process = 1
;

如果您使用 调用/运行存储过程EXEC,例如

EXEC [dbo].[LoggingTest]
@DateBegin = '2019-01-01',
@DateEnd = '2020-01-01',
@Person = 'person name',
@Unit = 'some unit';

然后,event_info将包含该EXEC查询的确切文本。我的意思是,正是上面的这段文字。EXEC [dbo].[LoggingTest] @DateBegin = '2019-01-01', @DateEnd = '2020-01-01', @Person = 'person name', @Unit = 'some unit';显然,它包含参数值,不需要解析任何东西或做任何其他技巧。记录日志就足够了。

凉爽的。事件类型将是“语言事件”。

不幸的是,如果您使用 RPC“正确”调用您的存储过程,这通常发生在 C# 代码中,那么您将得到的只是event_info. 像这样的东西:

YourDatabaseName.dbo.LoggingTest

将不会提及参数和/或它们的值:-( 事件类型将是“RPC 事件”。

parameters专栏smallint始终包含0在我的测试中。文档不清楚如何理解这一点。

于 2019-03-24T12:35:09.257 回答