22

我正在尝试调试一个用 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:尽管(自动)接受的答案确实跟踪了函数调用 - 非常有帮助,谢谢 - 它无助于找出传递给函数的参数。当然,这对于调试递归函数是必不可少的。如果我找到任何解决方案,我会发布...

4

9 回答 9

28

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

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

于 2008-12-09T12:50:56.137 回答
12

在 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

这对你来说足够了吗?

于 2009-02-17T14:48:18.760 回答
4

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

于 2009-02-16T11:21:36.113 回答
1

使用 SQL Profiler,我建议您在第一次添加事件时过度使用,这会让您对所需内容有所了解。如果不进行测试,我会为 SP:StmtStarted(或 Completed 或两者)、SQL:StmtStarted(再次 Completed 或 Both)添加事件。

于 2008-12-09T14:02:06.993 回答
0

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

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

于 2008-12-09T12:56:51.743 回答
0

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

于 2008-12-10T16:50:15.250 回答
0

也许您可以使用 SQL CLR 进行跟踪,如此处所述 如何登录 T-SQL

于 2009-02-13T13:33:29.810 回答
0

您能否获取您的函数,并制作它的第二个副本,但返回一个表类型,其中包含用于调试信息的附加列。

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

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

于 2009-02-16T15:31:54.793 回答
0

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

SQL间谍

SQL SPY 功能文档

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

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

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

于 2009-02-19T04:14:49.873 回答