Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Pourquoi la deuxième requête T-SQL s'exécute-t-elle beaucoup plus rapidement que la première lorsqu'elle est appelée par Reporting Services 2005 dans une application Web

Vous avez peut-être rencontré une requête qui a un problème avec le reniflage des paramètres, qui a à voir avec la façon dont Sql Server essaie d'optimiser votre plan d'exécution de requête, mais dans les cas où Reporting Services est impliqué, il le gâche complètement et le fait fonctionner incroyablement lentement.

J'ai eu un cas avec un rapport contenant deux requêtes complexes d'environ 150 lignes chacune mais qui s'exécutaient en 7 secondes dans mon environnement de développement - l'ensemble du rapport prenait moins de 10 secondes. Cependant, lorsqu'il était déployé sur le serveur SSRS de production, le rapport prenait plus de 7 minutes et expirait souvent, ce qui rendait le rapport inexécutable.

La plupart des informations sur ce problème en parlent en relation avec les procédures stockées. Ne rejetez pas cela parce que vous n'utilisez pas de procédures stockées (comme je l'ai fait pendant longtemps); il est également très pertinent pour les requêtes SQL directes.

La différence que vous voyez est donc que Sql Server crée deux plans d'exécution très différents car les deux requêtes sont structurées différemment.

Heureusement, la solution est très simple :placez les paramètres dans des variables internes et utilisez-les dans votre requête à la place. Je l'ai fait avec mon rapport et le rapport de production est revenu à 10 secondes comme la version de développement l'a fait dans Visual Studio.

Pour contourner le reniflage de paramètres pour votre première requête, vous devez la faire ressembler à ceci :

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;