打印动态参数值

时间:2011-05-05 20:11:06

标签: sql sql-server sql-server-2005 debugging sql-server-2008

我已经将动态SQL用于许多任务并不断遇到同样的问题:打印Dynamic T-SQL语句中使用的变量值。

EG:

Declare @SQL nvarchar(max), @Params nvarchar(max), @DebugMode bit, @Foobar int
select @DebugMode=1,@Foobar=364556423

set @SQL='Select @Foobar'
set @Params=N'@Foobar int'

if @DebugMode=1 print @SQL
exec sp_executeSQL @SQL,@Params
    ,@Foobar=@Foobar

上述代码的打印结果只是“选择@Foobar”。有没有办法动态打印值和&正在执行的sql的变量名?或者在进行打印时,将参数替换为实际值,以便SQL可以重新运行?

我使用创建一个或两个函数来完成类似的操作,但是使用数据类型转换,模式匹配截断问题和非动态解决方案。我很好奇其他开发人员如何在不手动手动打印每个变量的情况下解决此问题。

2 个答案:

答案 0 :(得分:3)

我不相信评估的语句是可用的,这意味着你的示例查询'Select @FooBar'永远不会像'Select 364556243'那样持久存在

即使在探查器跟踪中,您也会看到语句命中缓存为'(@Foobar int)select @foobar'

这是有道理的,因为使用sp_executesql的一大好处是它能够以可靠的形式缓存语句而不评估变量,否则如果它替换了变量并执行了该语句,我们就会看到执行计划膨胀。

已更新:这是正确方向的步骤

所有这些都可以通过输入(@ Statement,@ ParamDef,@ ParamVal)清理并包装在一个很好的函数中,并返回“准备好”的语句。我会留下一些作为练习,但是当你改进它时请回复!

使用此处的分割功能link

set nocount on;

declare @Statement  varchar(100),   -- the raw sql statement
        @ParamDef   varchar(100),   -- the raw param definition
        @ParamVal   xml             -- the ParamName -to- ParamValue mapping as xml


-- the internal params:
declare @YakId int,
        @Date datetime
select  @YakId = 99,
        @Date = getdate();


select  @Statement = 'Select * from dbo.Yak where YakId = @YakId and CreatedOn > @Date;',
        @ParamDef = '@YakId int, @Date datetime';

-- you need to construct this xml manually... maybe use a table var to clean this up
set @ParamVal = (   select *
                    from    (   select '@YakId', cast(@YakId as varchar(max)) union all
                                select '@Date', cast(@Date as varchar(max))
                            ) d (Name, Val)
                    for xml path('Parameter'), root('root')
                )

-- do the work
declare @pStage table (pName varchar(100), pType varchar(25), pVal varchar(100));
;with 
    c_p (p)
    as  (   select  replace(ltrim(rtrim(s)), ' ', '.')
            from    dbo.Split(',', @ParamDef)d
        ),
    c_s (pName, pType)
    as  (   select  parsename(p, 2), parsename(p, 1)
            from    c_p
        ),
    c_v (pName, pVal)
    as  (   select  p.n.value('Name[1]', 'varchar(100)'),
                    p.n.value('Val[1]', 'varchar(100)')
            from    @ParamVal.nodes('root/Parameter')p(n)
        )
insert into @pStage
    select  s.pName, s.pType, case when s.pType = 'datetime' then quotename(v.pVal, '''') else v.pVal end -- expand this case to deal with other types
    from    c_s s
    join    c_v v on
            s.pName = v.pName

-- replace pName with pValue in statement
select  @Statement = replace(@Statement, pName, isnull(pVal, 'null'))                       
from    @pStage
where   charindex(pName, @Statement) > 0;

print @Statement;

答案 1 :(得分:2)

关于大多数人如何做的话题,我只会说我做的事情:

  • 创建一个测试脚本,该脚本将使用各种有效和无效的输入来运行该过程。如果参数是一个整数,我会发送它'4'(而不是4),但我只会尝试1个古怪的字符串值,如'agd'。
  • 针对我正在做的事情,针对代表性大小和数据值分布的数据集运行值。使用您最喜欢的数据生成工具(市场上有几个好的工具)来加快速度。
  • 我通常会在更临时的基础上进行这样的调试,因此从SSMS结果窗口中收集结果是我需要的。

我能想到的最好的方法是使用SQL Trace捕获查询。如果在查询字符串中放置一些唯一的东西(作为注释),则很容易在跟踪中为它应用过滤器,这样就不会捕获超出需要的内容。

然而,并非所有的桃子和霜。

这仅适用于开发环境,可能 QA,具体取决于您的商店的严格程度。

如果查询需要很长时间才能运行,您可以通过在@DebugMode = 1时向查询字符串添加“TOP 1”,“WHERE 1 = 2”或类似的限制子句来缓解这种情况。否则,您可以最后等待一段时间才能完成。

对于长时间查询,您无法仅为调试模式添加查询字符串,您可以在StmtStarted事件中捕获命令文本,然后在获得命令后立即取消查询。

如果查询是INSERT / UPDATE / DELETE,则需要强制回滚,如果@DebugMode = 1并且您不希望发生更改。如果您当前没有使用显式事务,那么这将是额外的开销。

如果你走这条路,你可以实现一些自动化,让生活更轻松。您可以为跟踪创建和启动/停止操作创建模板。您可以将结果记录到文件或表中,并以编程方式从那里处理命令文本。