Sql server 如何使用SQL从文本中获取变量值?

Sql server 如何使用SQL从文本中获取变量值?,sql-server,tsql,dynamic,Sql Server,Tsql,Dynamic,第一部分 第二部分 DECLARE @A INT DECLARE @B NVARCHAR(20) SET @A=123 SET @B='@A' 此任务不接受在非动态SQL中直接引用@A 第2部分中的上述内容通常是我要做的。我知道这个变量超出了范围,不能像上面那样工作。我如何使用@B来获得@A的值 更新20190322: 我实际上忘记了这个问题,在C端而不是数据库上实现了日志记录,但我再次好奇这是否可行。同样,这需要是通用的,因为我希望将其放在任何存储过程的顶部,而不希望每个存储过程都对其进行

第一部分

第二部分

DECLARE @A INT
DECLARE @B NVARCHAR(20)

SET @A=123
SET @B='@A'
此任务不接受在非动态SQL中直接引用@A

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

更新20190322: 我实际上忘记了这个问题,在C端而不是数据库上实现了日志记录,但我再次好奇这是否可行。同样,这需要是通用的,因为我希望将其放在任何存储过程的顶部,而不希望每个存储过程都对其进行自定义;光标获取参数值时遇到问题。以下是一个工作示例:

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

EXEC SP_EXECUTESQL @SQL
--Should return 123

虽然您不必这样做,但方法如下:

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
您可以直接为@A赋值:

SET @B=@A

我不明白你问的全部问题,但你可以在sp_executesql上定义变量:


在四处搜索之后,我发现一个可以

从dm_exec_requests和dm_exec_sql_text获取运行过程代码

从dm_exec_input_buffer调用过程。我应该注意到,我对这方面不是很精通,我想我在某个地方读到过,它在SSMS之外的查询中不起作用

正如代码中已经看到的,可以在sys.parameters中找到参数

因此,我实现了一种这样做的方法。主要算法步骤如下:

使用相同的参数和默认值创建一个虚拟过程。将其主体更改为单选。通过在更高一级的原始过程中使用参数的动态xml连接,我们可以为伪过程提供一个值表,其中包括引号中的参数名称及其值无引号。 使用原始spc中使用的相同调用参数执行伪过程。将Exec结果集插入临时表。放弃虚拟进程 之后 现在我们有了一个包含参数名和值的临时表

除了光标中需要的实际值之外,大多数新代码都在开始之前。另外,为了避免激烈的解析,我使用了一个便宜的技巧,在开始之后添加了一个注释-postBeginParserMarker,这样我就知道过程从哪里开始了

EXEC SP_EXECUTESQL @SQL, N'@A INT', @A = @A

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

在以前的版本中有

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

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

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
如果使用EXEC调用/运行存储过程,例如

然后,event_info将包含EXEC查询的精确文本。我的意思是,正是上面这段文字。EXEC[dbo].[LoggingTest]@DateBegin='2019-01-01',@DateEnd='2020-01-01',@Person='Person name',@Unit='some Unit';显然,它包含参数值,不需要解析任何内容或执行任何其他技巧。这对于日志记录来说已经足够了

酷。事件类型将是语言事件

不幸的是,如果您使用RPC正确地调用存储过程(例如,在C代码中通常会发生这种情况),那么您将得到的只是存储过程的名称 事件信息。大概是这样的:

EXEC [dbo].[LoggingTest]
@DateBegin = '2019-01-01',
@DateEnd = '2020-01-01',
@Person = 'person name',
@Unit = 'some unit';
不会提及参数和/或其值:-事件类型为RPC事件


参数列是smallint,在我的测试中始终包含0。文档不清楚如何理解这一点。

执行这两段代码的上下文是什么?这是我正在做的一项更复杂任务的通用版本。我正在尝试编写一段通用的SQL代码,可以放在存储过程的顶部,记录存储过程名称、参数和参数值的信息。我在尝试获取参数值时遇到了一个障碍。我知道参数的名称,但我需要动态查询。我已经多次这样做了,现在唯一可行的方法是显式传递参数。@rbaryyoung-Hehe,我当然知道路由是开放的,但如果有一种方法不必以自定义方式对每个存储过程进行编码,那太好了。你是想避免知道@A的数据类型,还是根本不知道@A的名称?不能在静态SQL中直接使用@A。我添加了一条注释,解释了为什么。不能在静态SQL中直接使用@a。我添加了一条注释来解释为什么.Runs,但是考虑到我提供的注释代码中的示例用法,它没有显示变量值。我认为这是一个局部的解决方案。几个非常小的问题。这有点长,但工作得很好。@UnhandledExcepSean,当从C调用SP时它工作吗?在本例中使用RPC从sys.dm_exec_input_buffer中选择@newproccall=event_info应该只返回SP的名称,而不返回任何参数。我不知道该怎么做exec@newproccall正在获取参数。
它应该在程序或功能上失败。。。应为未提供的参数。我错过了什么吗?@VladimirBaranov我并没有测试过c,但它对我来说很好SSMS@UnhandledExcepSean,请让我们了解它在C或任何其他环境中的工作方式。在这些环境中,您调用SP时,不需要向服务器发送显式EXEC Proc@P1=123、@P2='qwerty'。VladimirBaranov您是对的。正如您所预料的,当从C作为参数化调用调用时,这会失败并出现错误。
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 [dbo].[LoggingTest]
@DateBegin = '2019-01-01',
@DateEnd = '2020-01-01',
@Person = 'person name',
@Unit = 'some unit';
YourDatabaseName.dbo.LoggingTest