sql >> Base de Datos >  >> RDS >> Sqlserver

¿Por qué la segunda consulta T-SQL se ejecuta mucho más rápido que la primera cuando Reporting Services 2005 la llama en una aplicación web?

Es posible que haya encontrado una consulta que tenga un problema con el rastreo de parámetros, que tiene que ver con la forma en que Sql Server intenta optimizar su plan de ejecución de consultas, pero en los casos en que Reporting Services está involucrado, lo estropea por completo y lo hace funcionar increíblemente lento.

Tuve un caso con un informe que tenía dos consultas complejas de alrededor de 150 líneas cada una, pero que se ejecutaron en 7 segundos en mi entorno de desarrollo:el informe completo tardó menos de 10 segundos. Sin embargo, cuando se implementó en el servidor SSRS de producción, el informe tardó más de 7 minutos y, a menudo, se agotó el tiempo de espera, lo que hizo que el informe no se pudiera ejecutar.

La mayor parte de la información sobre este problema habla de él en relación con los procedimientos almacenados. No descarte esto porque no está utilizando procedimientos almacenados (como lo hice durante mucho tiempo); también es muy relevante para las consultas Sql directas.

Entonces, la diferencia que está viendo es que Sql Server está creando dos planes de ejecución muy diferentes ya que las dos consultas están estructuradas de manera diferente.

Afortunadamente, la solución es muy simple:coloque los parámetros en variables internas y utilícelos en su consulta. Hice esto con mi informe y el informe de producción volvió a 10 segundos como lo hizo la versión de desarrollo en Visual Studio.

Para omitir el rastreo de parámetros para su primera consulta, haría que se viera así:

BEGIN
    -- Use internal variables to solve parameter sniffing issues
    DECLARE @StartDateInternal AS DATETIME;
    DECLARE @EndDateInternal AS DATETIME;
    DECLARE @SchoolIDInternal AS INT;
    DECLARE @GradeLevelInternal AS INT;

    -- Copy the parameters into the internal variables
    SET @StartDateInternal = @StartDate;
    SET @EndDateInternal = @EndDate;
    SET @SchoolIDInternal = @SchoolID;
    SET @GradeLevelInternal = @GradeLevel;

    -- Now use the internal variables in your query rather than the parameters
    SELECT 
        c.TeacherID, u.FName + ' ' + u.lname as Teacher, count(sb.behaviorID) as BxCount, 
        sb.behaviorID, b.BehaviorName, std.GradeID, gl.GradeLevel
    FROM 
        StudentBehaviors sb
    join 
        Classes c on sb.classid = c.classid
    join 
        StudentDetails std on sb.studentID = std.StudentID and std.RecordIsActive=1
    join 
        users u on c.TeacherID = u.UserID
    join 
        Behaviors b on sb.behaviorID = b.BehaviorID
    join 
        GradeLevels gl on std.GradeID = gl.GradeLevelID
    WHERE 
        sb.classdate between @StartDateInternal and @EndDateInternal
        and c.schoolid = @SchoolIDInternal
        and std.GradeID = @GradeLevelInternal
    GROUP BY 
        c.TeacherID, sb.behaviorID, b.BehaviorName, u.lname, u.FName, 
        std.GradeID, gl.GradeLevel
    ORDER BY 
        u.LName, sb.behaviorID;

END;