Tsql 如何跟踪T-SQL函数调用

Tsql 如何跟踪T-SQL函数调用,tsql,debugging,trace,user-defined-functions,Tsql,Debugging,Trace,User Defined Functions,我试图调试一个用T-SQL UDF(不要问)编写的相当复杂的公式计算器,它递归地调用自己(但通过一个中间函数间接地调用) 当然,我们有一个bug 现在,使用PRINT语句(可以通过实现InfoMessage事件的处理程序从ADO.NET读取),我可以模拟存储过程的跟踪 alter FUNCTION [dbo].[ufn_mjf](@i numeric(10)) RETURNS numeric(20) AS BEGIN declare @datapoint varchar(10)

我试图调试一个用T-SQL UDF(不要问)编写的相当复杂的公式计算器,它递归地调用自己(但通过一个中间函数间接地调用)

当然,我们有一个bug

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

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
对UDF执行相同操作将导致编译时消息:

Invalid use of side-effecting or time-dependent operator in 'PRINT' within a function.
我收到消息(PRINT执行一些操作,如重置
@@ROWCOUNT
,这在UDF中肯定是不允许的,但是我如何跟踪调用?我想打印此跟踪,这样我就可以研究它,而不会因为在调试器中单步执行调用而分心

编辑:我尝试过使用SQL分析器(这是我第一次使用),但我不知道跟踪的目的是什么:虽然我可以获得跟踪以输出发送到数据库的查询,但它们是不透明的,因为我无法深入到名为UDF的表达式:我可以跟踪调用的实际存储过程,但此过程调用的UDF没有列出。我是否缺少什么?我想没有


编辑#2:全部通过(自动-)accepted answer确实跟踪函数调用-非常有用,谢谢-它无助于找出传递给函数的参数。当然,这在调试递归函数时是必不可少的。如果我找到任何解决方案,我将发布…

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

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

我支持SQL Profiler的建议。请花一些时间进行设置,以便只记录您感兴趣的事件,以减少输出大小。您可以将跟踪输出到文件-i然后频繁地将该文件加载回表中以启用分析。(对于性能分析来说非常方便,尽管毫无疑问有人会告诉我2008年在somwehere中构建了这一切…)


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

使用SQL Profiler,我建议您在第一次添加事件时就过火,这样可以让您感觉到您需要什么ng我将为SP:StmtStarted(或Completed或两者)和SQL:StmtStarted(再次完成或两者)添加事件.

在过去,我必须获取UDF中的典型值,然后在单独的查询窗口中以直接SQL的形式运行UDF部分,而不是使用典型值作为变量集的UDF,并使用declare和set语句。如果它是从表中运行的,而不是只有一个值,我将设置临时表或表变量B使用输入值进行编译,然后通过游标在UDF中的sql中运行它们(但同样是直接sql而不是UDF)。通过运行直接sql,您可以在其中使用打印语句来查看发生了什么。我知道这很痛苦,但它是有效的。(在创建/调试触发器时,我会经历一个类似的过程,使用我的测试值设置插入和删除,然后测试我打算放入触发器中的代码,然后用“无”替换“全局”,并添加创建触发器代码。)

也许您可以使用SQL CLR来执行此处所述的跟踪

看起来像您所需要的,但它仅在Visual Studio的team/pro版本中可用。

您可以使用您的函数,并对其进行第二次复制,但返回一个带有附加列的表类型作为调试信息

例如,下面的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)

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

在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

这对您来说够了吗?

我使用SQL SPY,它可以完成您正在寻找的功能以及更多功能

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

此功能是为MS SQL Server 2005\2008设计的,但将在有限的范围内与MS SQL Server 2000配合使用。它能够记录和报告传入的SQL。如何使用这些功能:请参阅


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

我已经尝试过了,但它并没有真正起作用:尽管跟踪显示了调用的原始存储过程,但实际的UDF(表达式)并未列出,因此我仍然只是在看一个黑盒。嗯,我面前没有SQL Profiler(当前的gig基于Oracle)。也许有人可以找到正确的事件来设置以获取UDF信息(或者告诉ua这是不可能的)。存储过程:SP:StmtStarting或SP:StmtCompleted您也可以添加局部变量并设置@debug='i am here',这将显示在profilerSure中,但我感兴趣的不是存储过程,而是UDF。这些是T-SQL中完全不同的beast,具有非常严格的限制…SP:StmtStarting也处理函数。对于例如,请进一步查看我的答案。是的,请查看下面Matthieu的答案,其中解释了您需要在探查器设置中添加哪些事件。默认情况下,它只捕获少数事件,而这些事件不是您进行故障排除所需的事件。站点已停止运行,可能会变成:-我认为这无法回答问题。