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