如何使用SQL从文字中获取变量值?

时间:2013-09-03 19:12:27

标签: sql-server tsql dynamic

第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 个答案:

答案 0 :(得分:5)

我不明白你问的所有事情,但你可以在sp_executesql上定义变量:

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

答案 1 :(得分:1)

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

SET @B=@A

您可以直接指定@A的值:

 DECLARE @SQL NVARCHAR(MAX)
 SET @SQL='SELECT ' + Convert(varchar(50),@A)

答案 2 :(得分:1)

搜索后,我发现一个可以

  1. 具有dm_exec_requestsdm_exec_sql_text

  2. 中正在运行的过程代码
  3. 具有来自dm_exec_input_buffer的过程调用。我应该指出,我对此并不十分精通,而且我认为我在某处阅读过的文章不适用于在SSMS之外完成的查询。...

  4. 在代码中已经看到,可以在sys.parameters

  5. 中找到参数

因此,我实现了一种方法。算法的主要步骤是:

  
      
  1. 使用相同的参数和默认值创建一个虚拟proc。将其主体更改为单个SELECT。通过在原始proc上使用一个动态xml级联的参数,该过程高一级,我们可以为虚拟proc提供一个VALUES表,其中包含参数名称(用引号引起来)和它们的值(没有引号) 。
  2.   
  3. 通过使用原始spc中使用的相同调用参数来执行虚拟proc。将INSERT的{​​{1}}结果集放入临时表。删除虚拟进程   之后。
  4.   

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

大多数新代码都在您开始之前,除了您在游标中所需的实际值之外。另外,为了避免进行激烈的解析,我使用了(便宜的)技巧,在EXEC之后添加了注释--postBeginParserMarker,以便我知道proc从哪里开始...

BEGIN

答案 3 :(得分: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。文档尚不清楚如何理解这一点。