Вопрос

Я пытаюсь отладить довольно сложный оценщик формул, написанный в 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 (для меня это был первый раз), но я не могу понять, что отслеживать:Хотя я могу получить трассировку для вывода запросов, отправленных в базу данных, они непрозрачны в том смысле, что я не могу перейти к выражениям-UDF, называемым:Я могу отследить фактическую вызванную хранимую процедуру, но пользовательские функции, вызванные этой процедурой, не указаны.Я что-то пропустил?Я думаю, нет...

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

Возможно, вы можете использовать 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-шпион

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top