Pregunta

Estoy tratando de depurar un evaluador de fórmulas bastante complicado escrito en UDF T-SQL (no pregunte) que recursivamente (pero indirectamente a través de una función intermedia) se llama a sí mismo, bla, bla.

Y, por supuesto, tenemos un error.

Ahora, usando las declaraciones PRINT (que luego se pueden leer desde ADO.NET mediante la implementación de un controlador para el evento InfoMessage), puedo simular un seguimiento de los procedimientos almacenados.

Hacer lo mismo para UDF da como resultado un mensaje de tiempo de compilación:

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

Recibo el mensaje (PRINT hace algunas cosas como restablecer @@ROWCOUNT que definitivamente es un no-no en UDF, pero ¿cómo puedo rastrear las llamadas? Quiero que se imprima este rastreo, así puedo estudiarlo sin distraerse pasando por las llamadas en el depurador ...

EDITAR: he intentado utilizar el Analizador de SQL (esta fue la primera vez para mí), pero no puedo averiguar qué rastrear: aunque puedo obtener el rastreo para genera las consultas enviadas a la base de datos, son opacas en el sentido de que no puedo profundizar en los Expression-UDF llamados: puedo rastrear el Procedimiento almacenado real invocado, pero los UDF llamados por este procedimiento no están listados. ¿Me estoy perdiendo de algo? Supongo que no ...

EDIT # 2: Aunque la respuesta (auto) aceptada rastrea las llamadas de función, muy útil, gracias, no ayuda a descubrir qué parámetros se pasaron a la función. Esto, por supuesto, es esencial en depuración funciones recursivas. Publicaré si encuentro alguna solución ...

¿Fue útil?

Solución

¿Por qué no usar SQL Profiler con eventos de nivel de declaración agregados?

Editar : agregue eventos para procedimientos almacenados: SP: inicio de SP o SP: finalizado Use variables para depurar si es necesario, es decir, establezca @ debug = 'estoy aquí'; Los UDF, aunque no son procedimientos almacenados técnicamente, se rastrearán con los eventos de nivel de declaración.

Otros consejos

En el generador de perfiles SQL, necesita: SP: Inicio, SP: StmtStarting, SP: Completado, SQL: BatchStarting. Luego, obtiene cada entrada, sale de las funciones / procedimientos almacenados.

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

con esto, obtengo:

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

¿es suficiente para ti?

Esto se parece a lo que necesita pero solo está disponible en las versiones de equipo / profesional de Visual Studio.

Use el Analizador de SQL, le recomiendo que se exceda al agregar eventos la primera vez, lo que le permitirá tener una idea de lo que necesita. Sin probar, agregaría los eventos para SP: StmtStarted (o Completed o ambos), SQL: StmtStarted (nuevamente Completed o Both).

Secundo la sugerencia de SQL Profiler. Tómese un tiempo para configurarlo de modo que solo los eventos que le interesan se registren para reducir el tamaño de salida. Puede enviar la traza a un archivo; con frecuencia, he cargado ese archivo nuevamente en una tabla para permitir el análisis. (extremadamente útil para el análisis de rendimiento, aunque sin duda alguien me dirá que 2008 tiene todo esto incorporado en algún lugar ...)

A veces no tendrá permisos para ejecutar SQL Profiler, ya que ralentiza el servidor; solicite a su DBA que le otorgue permiso en su servidor Dev. No deberían tener ningún problema con eso.

Bueno, en el pasado tuve que tomar valores típicos que estarían en la UDF y luego ejecutar solo la parte udf en una ventana de consulta separada como SQL directo, no un udf usando los valores típicos como variables establecidas con una declaración y un Establecer declaración. Si se ejecuta desde una tabla en lugar de tener solo un valor, configuraría una tabla temporal o variable de tabla con los valores de entrada y luego los ejecutaría a través del sql en el UDF (pero nuevamente como SQL directo no un UDF) a través de un cursor. Al ejecutar SQL directo, podría tener declaraciones impresas para ver qué está sucediendo. Sé que esto es un dolor, pero funciona. (Realizo un proceso similar al crear / depurar activadores, configuro # insertado y # eliminado con mis valores de prueba y luego pruebo el código que pretendo poner en el activador, luego global reemplaza el # por nada y agrego el código de activación de creación. )

Tal vez pueda usar SQL CLR para hacer el seguimiento como se describe aquí Cómo iniciar sesión en T-SQL

¿Puede tomar su función y hacer una segunda copia, pero devolviendo un tipo de tabla con una columna adicional para su información de depuración?

Por ejemplo, la función mySum a continuación

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)

Se convertiría en

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)

No es una solución ideal, pero es útil solo para devolver texto para ayudar a localizar un error.

Uso SQL SPY que hace lo que estás buscando y más.

SQL SPY

Documentación de funciones SQL SPY

El sniffer SQL entrante de SQL SPY muestra el código SQL entrante de cada conexión (incluye el seguimiento de sentencias DDL y DML)

Esta característica está diseñada para MS SQL Server 2005 \ 2008, pero funcionará con MS SQL Server 2000 en un alcance limitado. Tiene la capacidad de grabar e informar sobre SQL entrante. Cómo usar las funciones: ver

Divulgación: soy parte del equipo SQL SPY.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top