Как отслеживать вызовы функций T-SQL
Я пытаюсь отлаживать довольно сложный оценщик формулы, написанный в UDF T-SQL (не спрашивайте), что рекурсивно (но косвенно через промежуточную функцию) вызывает себя, blah, blah.
И, конечно, у нас есть ошибка.
Теперь, используя инструкции 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: я могу отслеживать вызвавшуюся хранимую процедуру, но UDF, вызываемые этой процедурой, не указаны. Я что-то упускаю? Думаю, не...
РЕДАКТИРОВАТЬ № 2:. Несмотря на то, что принятый (автоматический) ответ проверяет вызовы функций - очень полезно, спасибо - это не помогает определить, какие параметры были переданы функции. Это, конечно, важно в рекурсивных функциях отладки. Я отправлю сообщение, если найду какое-нибудь решение...
Ответы
Ответ 1
Почему бы не использовать SQL Profiler с добавлением событий уровня инструкций?
Изменить. Добавить события для хранимых процедур: SP: запуск Stm или SP: Stmt Completed
Используйте переменные для отладки при необходимости, т.е. Установите @debug = 'i am here'; UDF, а не технически хранимые процедуры, будут отслеживаться с событиями уровня инструкций.
Ответ 2
В профилировщике 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
достаточно для вас?
Ответ 3
Это выглядит так, как вам нужно, но доступно только в командных/про-версиях Visual Studio.
Ответ 4
Используйте SQL Profiler, я рекомендую вам переходить на добавление событий в первый раз, что позволит вам почувствовать, что вам нужно. Без тестирования я бы добавил события для SP: StmtStarted (или Completed или оба), SQL: StmtStarted (снова завершено или оба).
Ответ 5
Во-вторых, предложение SQL Profiler. Потратьте некоторое время, чтобы настроить его, чтобы только события, которые вас интересуют, регистрируются, чтобы сократить выходной размер. Вы можете вывести трассировку в файл - я часто загружал этот файл обратно в таблицу, чтобы включить анализ. (чрезвычайно удобный для анализа производительности, хотя, несомненно, кто-то скажет мне, что в 2008 году все это было построено в некотором роде...)
Иногда у вас не будет разрешений на запуск SQL Profiler, так как он замедляет работу сервера - попросите своего администратора базы данных предоставить вам разрешение на ваш сервер Dev. У них не должно быть проблем с этим.
Ответ 6
В прошлом мне приходилось принимать типичные значения, которые были бы в UDF, а затем запускать только часть udf в отдельном окне запроса, поскольку прямой SQL не является udf, используя типичные значения как переменные, заданные с объявлением, и set statement. Если он запускается из таблицы вместо того, чтобы иметь только одно значение, я бы установил временную таблицу или таблицу с входными значениями, а затем запустил их через sql в UDF (но опять же, как прямой SQL, а не UDF) через курсор. Запустив прямой SQL, вы можете иметь в нем инструкции печати, чтобы увидеть, что происходит. Я знаю, что это боль, но она работает. (Я запускаю процесс simliar при создании/отладке триггеров, устанавливаю #incerted и #deleted с моими тестовыми значениями, а затем тестирую код, который я намерен помещать в триггер, затем глобально заменяю # ничем и добавляем создающий триггер-код. )
Ответ 7
Возможно, вы можете использовать SQL CLR для выполнения трассировки, как описано здесь
Как войти в T-SQL
Ответ 8
Можете ли вы взять свою функцию и сделать вторую ее копию, но верните тип таблицы с дополнительным столбцом для вашей информации об отладке.
Например, функция 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)
Не идеальное решение, но полезно просто вернуть некоторый текст, чтобы помочь отследить ошибку.
Ответ 9
Я использую SQL SPY, который делает то, что вы ищете, и многое другое.
SQL SPY
Документация по SQL SPY
SQL SPY Входящий SQL Sniffer показывает входящий SQL-код каждого соединения (включает отслеживание операторов DDL и DML)
Эта функция предназначена для MS SQL Server 2005\2008, но будет работать с MS SQL Server 2000 в ограниченной области. Он имеет возможность записывать и сообщать о входящем SQL. Как использовать функции: См.
Раскрытие информации: Я являюсь частью команды SQL SPY.