【问题标题】:How to trace T-SQL function calls如何跟踪 T-SQL 函数调用
【发布时间】:2010-09-26 00:58:13
【问题描述】:

我正在尝试调试一个用 T-SQL UDF(不要问)编写的相当复杂的公式评估器,它递归地(但通过中间函数间接地)调用自身,等等。

当然,我们有一个错误。

现在,使用 PRINT 语句(然后可以通过实现 InfoMessage 事件的处理程序从 ADO.NET 读取),我可以模拟存储过程的跟踪。

对 UDF 执行相同操作会导致编译时消息:

Invalid use of side-effecting or time-dependent operator in 'PRINT' within a function.

我收到消息(PRINT 做了一些事情,比如重置 @@ROWCOUNT,这在 UDF 中绝对是一个禁忌,但是我如何跟踪调用?我想打印出这个跟踪,这样我就可以研究它了不会因为单步调试调试器中的调用而分心...

编辑:我尝试使用 SQL Profiler(这对我来说是第一次),但我不知道要跟踪什么:虽然我可以得到跟踪输出发送到数据库的查询,它们是不透明的,因为我无法深入到调用的 Expression-UDF:我可以跟踪调用的实际存储过程,但未列出此过程调用的 UDF。我错过了什么吗?估计不是……

编辑#2:尽管(自动)接受的答案确实会跟踪函数调用 - 非常有帮助,谢谢 - 它无助于找出 传递了哪些参数 到功能。当然,这对于调试递归函数是必不可少的。如果我找到任何解决方案,我会发布......

【问题讨论】:

  • 是的,请查看下面 Matthieu 的答案,其中解释了您需要在 Profiler 设置中添加哪些事件。默认情况下,它只捕获一些事件,它们不是您进行故障排除所需的。

标签: tsql debugging trace user-defined-functions


【解决方案1】:

为什么不使用添加了语句级事件的 SQL Profiler?

编辑:为存储过程添加事件:SP:Stmt Starting 或 SP:Stmt Completed 如果需要,使用变量进行调试,即设置 @debug='i am here'; UDF,虽然在技术上不是存储过程,但将使用语句级事件进行跟踪。

【讨论】:

  • 当然,但我感兴趣的不是存储过程,而是 UDF。这些是 T-SQL 中完全不同的野兽,并且有非常严格的限制......
  • SP:StmtStarting 也处理函数。例如,请参阅我的答案。
【解决方案2】:

在 SQL 分析器中,您需要:SP:Starting、SP:StmtStarting、SP:Completed、SQL:BatchStarting。然后,你得到每个入口,从函数/存储过程中退出。

alter FUNCTION [dbo].[ufn_mjf](@i numeric(10))
    RETURNS numeric(20) 
AS
BEGIN
declare @datapoint varchar(10)

    set @datapoint = 'hello world'

    return @i
END
go
drop table foo
go
create table dbo.foo ( foo_id numeric(10)) 
go
delete from foo
insert into foo ( foo_id ) values ( 1 )
insert into foo ( foo_id ) values ( 2 )

select foo_id, dbo.ufn_mjf(foo_id) from foo

有了这个,我得到:

SQL:BatchStarting   alter FUNCTION [dbo].[ufn_mjf](@i numeric(10))
SQL:BatchStarting   drop table foo
SQL:BatchStarting   create table dbo.foo ( foo_id numeric(10)) 
SQL:BatchStarting   delete from foo
    insert into foo ( foo_id ) values ( 1 )
    insert into foo ( foo_id ) values ( 2 )
    select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:Starting select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:StmtStarting set @datapoint = 'hello world'
SP:StmtStarting return @i
SP:Completed    select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:Starting select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:StmtStarting set @datapoint = 'hello world'
SP:StmtStarting return @i
SP:Completed    select foo_id, dbo.ufn_mjf(foo_id) from foo

你觉得够了吗?

【讨论】:

    【解决方案3】:

    This 看起来像您需要的,但它仅在 Visual Studio 的团队/专业版本中可用。

    【讨论】:

      【解决方案4】:

      使用 SQL 事件探查器,我建议您在第一次添加事件时就过火了,这会让您感觉自己需要什么。如果不进行测试,我会为 SP:StmtStarted(或 Completed 或两者)、SQL:StmtStarted(再次 Completed 或 Both)添加事件。

      【讨论】:

        【解决方案5】:

        我赞同 SQL Profiler 的建议。花一些时间进行设置,以便只记录您感兴趣的事件以减少输出大小。您可以将跟踪输出到文件 - 我经常将该文件加载回表中以启用分析。 (对于性能分析非常方便,尽管毫无疑问有人会告诉我 2008 年已将这一切都内置在 somwehere...)

        有时您没有运行 SQL Profiler 的权限,因为它会降低服务器速度 - 请您的 DBA 授予您对开发服务器的权限。他们不应该有任何问题。

        【讨论】:

        • 我已经尝试过了,但它并没有真正起作用:虽然跟踪显示了调用的原始存储过程,但没有列出实际的 UDF(表达式),所以我仍然只是在看一个黑匣子。
        • 嗯,我面前没有 SQL Profiler(当前演出是基于 Oracle 的)。也许有人可以找到正确的事件来设置以获取 UDF 信息(或告诉 ua 这是不可能的)。
        • 存储过程:SP:StmtStarting 或 SP:StmtCompleted 您也可以添加局部变量并设置 @debug='i am here' 这将显示在分析器中
        【解决方案6】:

        在过去,我不得不采用 UDF 中的典型值,然后在单独的查询窗口中只运行 udf 部分作为直接 SQL,而不是使用典型值作为变量集的 udf,并使用声明和设置语句。如果它是从表中运行而不是只有一个值,我将使用输入值设置一个临时表或表变量,然后通过 UDF 中的 sql 运行它们(但再次作为直接 SQL 而不是 UDF)通过光标。通过直接运行 SQL,您可以在其中包含打印语句以查看发生了什么。我知道这很痛苦,但它确实有效。 (我在创建/调试触发器时经历了一个类似的过程,使用我的测试值设置#inserted 和#deleted,然后测试我打算放入触发器的代码,然后将# 全局替换为空并添加创建触发器代码。 )

        【讨论】:

          【解决方案7】:

          也许您可以使用 SQL CLR 进行跟踪,如此处所述 How to log in T-SQL

          【讨论】:

            【解决方案8】:

            您能否获取您的函数,并对其进行第二次复制,但返回一个表类型,其中包含用于调试信息的附加列。

            比如下面的mySum函数

            CREATE FUNCTION mySum
            (   
                @param1 int,
                @param2 int
            )
            RETURNS INT AS
            BEGIN
                DECLARE @mySum int
            
                SET @mySum = @param1
            
                SET @mySum = @mySum + @param2
            
                RETURN @mySum
            
            END
            GO
            SELECT dbo.mySum(1, 2)
            

            会变成

            CREATE FUNCTION mySumDebug
            (   
                @param1 int,
                @param2 int
            )
            RETURNS @myTable TABLE
            (
                [mySum] int,
                [debug] nvarchar(max)
            )
            AS
            BEGIN
                DECLARE @debug nvarchar(max)
            
                SET @debug = 'Declare @mySum variable. '
                DECLARE @mySum int
            
                SET @debug = @debug + 'Set @mySum = @param1(' + CONVERT(nvarchar(50), @param1) + ') '
                SET @mySum = @param1
            
            
                SET @debug = @debug + 'Add @param2(' + CONVERT(nvarchar(50), @param2) + ') to @mySum(' + CONVERT(nvarchar(50), @mySum) + ') '
                SET @mySum = @mySum + @param2
            
                SET @debug = @debug + 'Return @mySum variable. '
            
                INSERT @myTable (mySum, debug) VALUES (@mySum, @debug)
            
                RETURN
            END
            GO
            SELECT mySum, debug FROM dbo.mySumDebug(1, 2)
            

            不是一个理想的解决方案,但仅用于返回一些文本以帮助追踪错误。

            【讨论】:

              【解决方案9】:

              我使用 SQL SPY,它可以满足您的需求以及更多功能。

              SQL SPY

              SQL SPY Feature Documentation

              SQL SPY 的 Incoming SQL Sniffer 显示每个连接的传入 SQL 代码(包括 DDL 和 DML 语句跟踪)

              此功能专为 MS SQL Server 2005\2008 设计,但可在有限范围内与 MS SQL Server 2000 一起使用。它具有记录和报告传入 SQL 的能力。如何使用功能:见

              披露:我是 SQL SPY 团队的一员。

              【讨论】:

              • 网站已死,可能变成了:sqlspy.codeplex.com - 不过我认为这不能回答问题。
              猜你喜欢
              • 1970-01-01
              • 2014-05-28
              • 1970-01-01
              • 2018-10-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-05-23
              相关资源
              最近更新 更多