Как отслеживать вызовы функций T-SQL

Я пытаюсь отладить довольно сложный вычислитель формул, написанный в T-SQL UDFs (не спрашивайте), что рекурсивно (но косвенно через промежуточную функцию) называет себя, бла-бла.

и, конечно, у нас есть ошибка.

теперь, используя операторы печати (которые затем можно прочитать из ADO.NET реализуя обработчик для события InfoMessage), я могу имитировать трассировку для хранимых процедур.

делать то же самое для UDF приводит к сообщение времени компиляции:

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

Я получаю сообщение (печать делает некоторые вещи, такие как сброс @@ROWCOUNT который определенно является no-no в UDFs, но как я могу проследить через вызовы? Я хочу, чтобы эта трассировка была распечатана, чтобы я мог изучить ее, не отвлекаясь на вызовы в отладчике...

EDIT: Я попытался использовать SQL Profiler (это был первый раз для меня), но я не могу понять, что отслеживать: хотя я могу получить трассировка для вывода запросов, отправленных в базу данных, они непрозрачны в том смысле, что я не могу перейти к выражению-UDFs: я могу отслеживать фактическую вызванную хранимую процедуру, но UDFs, вызванные этой процедурой, не перечислены. Я что-то упускаю? Думаю, нет...

редактировать #2: хотя (авто-)принятый ответ отслеживает вызовы функций-очень полезно, спасибо - это не помогает в выяснении, какие параметры были принят к функция. Это, конечно, существенно в отладка рекурсивные функции. Я отправлю сообщение, если найду какое-либо решение...

9 ответов


Почему бы не использовать SQL Profiler с добавлением событий уровня оператора?

редактировать: добавление событий для хранимых процедур:запуск SP: Stmt или завершение SP:Stmt Используйте переменные для отладки, если это необходимо, т. е. set @debug= 'i am here'; UDF, хотя и не технически хранимые процедуры, будут отслеживаться с событиями уровня оператора.


в профилировщике 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

этого достаточно для вас?


этой похоже, что вам нужно, но оно доступно только в версиях team / pro Visual Studio.


используйте SQL Profiler, я рекомендую вам пойти за борт на добавление событий в первый раз, который позволит вам почувствовать, что вам нужно. Без тестирования я бы добавил события для SP:StmtStarted (или завершено или оба), SQL: StmtStarted (снова завершено или оба).


Я поддерживаю предложение SQL Profiler. Потратьте некоторое время, чтобы настроить его так, чтобы только события, которые вас интересуют, регистрировались для сокращения размера вывода. Вы можете вывести трассировку в файл - я часто загружал этот файл обратно в таблицу, чтобы включить анализ. (очень удобно для анализа производительности, хотя, без сомнения, кто-то скажет мне, что 2008 все это построено в somwehere...)

иногда у вас не будет разрешений на запуск SQL Profiler, поскольку он замедляет работу сервера down-попросите DBA предоставить вам разрешение на вашем Dev-сервере. У них не должно быть никаких проблем с этим.


Ну, в прошлом мне приходилось принимать типичные значения, которые были бы в UDF, а затем запускать только часть udf в отдельном окне запроса как прямой SQL, а не udf, используя типичные значения в качестве переменных, заданных с помощью оператора declare и set. Если он запускается из таблицы вместо того, чтобы иметь только одно значение, я бы настроил временную таблицу или переменную таблицы с входными значениями, а затем запустил их через sql в UDF (но снова как прямой SQL не UDF) через курсор. Запустив прямо SQL вы можете иметь инструкции печати в нем, чтобы увидеть, что происходит. Я знаю, это больно, но это работает. (Я прохожу через процесс simliar при создании / отладке триггеров, установке #inserted и #deleted с моими тестовыми значениями, А затем тестирую код, который я намерен поместить в триггер, затем глобальный замените # ничем и добавьте код create trigger.)


возможно, вы можете использовать SQL CLR для выполнения трассировки, как описано здесь Как войти в T-SQL


можете ли вы взять свою функцию и сделать ее вторую копию, но возвращая тип таблицы с дополнительным столбцом для отладочной информации.

например, функция 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 SPY, который делает то, что вы ищете, и многое другое.

SQL SPY

SQL SPY Feature Documentation

SQL SPY'S Incoming SQL Sniffer показывает входящий SQL-код каждого соединения (включая отслеживание инструкций DDL и DML)

эта функция предназначена для MS SQL Server 2005\2008, но будет работать с MS SQL Server 2000 в ограниченной области. Он имеет возможность записывать и сообщать о входящих SQL. Как использовать функции: см

раскрытие информации: я являюсь частью команды SQL SPY.