Как отследить вызовы функций T-SQL - PullRequest
22 голосов
/ 09 декабря 2008

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

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

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

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

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

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

РЕДАКТИРОВАТЬ: Я пытался использовать SQL Profiler (это был первый раз для меня), но я не могу понять, что для трассировки: хотя я могу получить трассировку для вывода запросы, отправленные в базу данных, являются непрозрачными в том смысле, что я не могу детализировать до вызываемых Expression-UDF: я могу проследить фактическую вызванную хранимую процедуру, но UDF, вызываемые этой процедурой, не перечислены. Я что-то пропустил? Я думаю, нет ...

РЕДАКТИРОВАТЬ # 2: Несмотря на то, что (автоматически) принятый ответ действительно отслеживает вызовы функций - очень полезно, спасибо - это не помогает выяснить, какие параметры были переданы в функция. Это, конечно, важно в отладке рекурсивных функций. Я опубликую, если найду какое-либо решение ...

Ответы [ 9 ]

28 голосов
/ 09 декабря 2008

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

Редактировать : Добавить события для хранимых процедур: SP: запуск STMT или SP: выполнение Stmt При необходимости используйте переменные для отладки, то есть set @ debug = 'я здесь'; UDF, хотя и не являются технически хранимыми процедурами, будут отслеживаться событиями уровня операторов.

12 голосов
/ 17 февраля 2009

В профилировщике SQL необходимо: SP: запуск, SP: StmtStarting, SP: завершено, 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

тебе этого достаточно?

4 голосов
/ 16 февраля 2009

Это выглядит как то, что вам нужно, но оно доступно только в версиях Visual Studio для разработчиков и профессионалов.

1 голос
/ 09 декабря 2008

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

0 голосов
/ 19 февраля 2009

Я использую SQL SPY, который делает то, что вы ищете и многое другое.

SQL SPY

Документация по функциям SQL SPY

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

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

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

0 голосов
/ 16 февраля 2009

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

Например, функция 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)

Не идеальное решение, но полезно просто вернуть некоторый текст, чтобы помочь отследить ошибку.

0 голосов
/ 13 февраля 2009

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

0 голосов
/ 10 декабря 2008

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

0 голосов
/ 09 декабря 2008

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

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

...