我正在尝试调试一个用T-SQL UDF编写的相当复杂的公式求值程序(不要问)递归(但间接通过中间函数)调用自己,blah,blah。
当然,我们有一个错误。
现在,使用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:虽然(自动)接受的答案会跟踪函数调用 - 非常有帮助,谢谢 - 它无助于找出传递的参数到功能。当然,这在调试递归函数中至关重要。如果我发现任何溶剂,我会发布...
答案 0 :(得分:28)
为什么不使用SQL Profiler添加语句级事件?
编辑:为存储过程添加事件:SP:Stmt Starting或SP:Stmt Completed 如果需要,使用变量进行调试,即设置@ debug ='我在这里'; UDF虽然不是技术上存储的过程,但会跟踪语句级事件。
答案 1 :(得分: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
对你来说够了吗?
答案 2 :(得分:4)
This看起来像你需要的,但它只适用于Visual Studio的团队/专业版。
答案 3 :(得分:1)
使用SQL事件探查器,我建议您在第一次添加事件时过度使用,以便让您了解所需内容。如果没有测试,我会为SP添加事件:StmtStarted(或已完成或两者),SQL:StmtStarted(再次完成或两者)。
答案 4 :(得分:0)
我是SQL Profiler建议的第二个。花一些时间进行设置,以便只记录您感兴趣的事件以减少输出大小。您可以将跟踪输出到文件 - 我经常将该文件加载回表中以启用分析。 (对于性能分析来说非常方便,但毫无疑问有人会告诉我,2008年所有这些都是建立在somwehere ...)
有时您无权运行SQL事件探查器,因为它会降低服务器速度 - 请让您的DBA授予您Dev开发服务器的权限。他们应该没有任何问题。
答案 5 :(得分:0)
在过去,我不得不采用UDF中的典型值,然后在单独的查询窗口中仅运行udf部分作为直接SQL而不是udf,使用典型值作为使用声明和a设置的变量设定声明。如果它是从一个表运行而不是只有一个值,我会设置一个带有输入值的临时表或表变量,然后通过UDF中的sql运行它们(但是再次作为直接SQL而不是UDF)通过光标。通过运行直接SQL,您可以在其中包含打印语句以查看发生的情况。我知道这是一种痛苦,但它确实有效。 (我在创建/调试触发器时会经历一个简单的过程,使用我的测试值设置#inserted和#deleted,然后测试我打算放入触发器的代码,然后全局替换#with no,并添加create trigger代码。 )
答案 6 :(得分:0)
也许您可以使用SQL CLR来执行此处所述的跟踪 How to log in T-SQL
答案 7 :(得分: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)
不是理想的解决方案,但仅用于返回一些文本以帮助追踪错误。
答案 8 :(得分:0)
我使用SQL SPY来完成你正在寻找的东西。
SQL SPY的Incoming SQL Sniffer显示每个连接的传入SQL代码(包括DDL和DML语句跟踪)
此功能是为MS SQL Server 2005 \ 2008设计的,但可以在有限范围内与MS SQL Server 2000一起使用。它能够记录和报告传入SQL。如何使用这些功能:见
披露:我是SQL SPY团队的一员。