Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Por que a segunda consulta T-SQL é executada muito mais rápido do que a primeira quando chamada pelo Reporting Services 2005 em um aplicativo da web


Você pode ter encontrado uma consulta que tem um problema com o sniffing de parâmetros, que tem a ver com a forma como o Sql Server tenta otimizar seu plano de execução de consulta, mas nos casos em que o Reporting Services está envolvido, atrapalha completamente e faz com que ele seja executado incrivelmente devagar.

Eu tive um caso com um relatório que tinha duas consultas complexas de cerca de 150 linhas cada, mas que foram executadas em 7 segundos no meu ambiente de desenvolvimento - o relatório inteiro levou menos de 10 segundos. No entanto, quando implantado no servidor SSRS de produção, o relatório levava mais de 7 minutos e, muitas vezes, expirava, tornando o relatório impossível de ser executado.

A maioria das informações sobre esse problema fala sobre isso em relação a procedimentos armazenados. Não descarte isso porque você não está usando procedimentos armazenados (como eu fiz por muito tempo); também é muito relevante para consultas Sql diretas.

Portanto, a diferença que você está vendo é que o Sql Server está criando dois planos de execução muito diferentes, pois as duas consultas são estruturadas de maneira diferente.

Felizmente, a solução é muito simples:coloque os parâmetros em variáveis ​​internas e use-as em sua consulta. Eu fiz isso com meu relatório e o relatório de produção voltou para 10 segundos como a versão de desenvolvimento fez no Visual Studio.

Para ignorar o sniffing de parâmetros para sua primeira consulta, você faria com que ficasse assim:
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;