Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Warum wird die zweite T-SQL-Abfrage viel schneller ausgeführt als die erste, wenn sie von Reporting Services 2005 in einer Webanwendung aufgerufen wird

Möglicherweise sind Sie auf eine Abfrage gestoßen, die ein Problem mit dem Parameter-Sniffing hat, was damit zu tun hat, wie Sql Server versucht, Ihren Abfrageausführungsplan zu optimieren, aber in Fällen, in denen Reporting Services beteiligt ist, ihn völlig durcheinander bringt und unglaublich langsam läuft.

Ich hatte einen Fall mit einem Bericht, der zwei komplexe Abfragen mit jeweils etwa 150 Zeilen hatte, aber in meiner Entwicklungsumgebung in 7 Sekunden lief – der gesamte Bericht dauerte weniger als 10 Sekunden. Bei der Bereitstellung auf dem Produktions-SSRS-Server dauerte der Bericht jedoch mehr als 7 Minuten und es kam häufig zu einer Zeitüberschreitung, wodurch der Bericht nicht mehr ausgeführt werden konnte.

Die meisten Informationen zu diesem Problem beziehen sich auf gespeicherte Prozeduren. Verwerfen Sie dies nicht, weil Sie keine gespeicherten Prozeduren verwenden (wie ich es lange Zeit getan habe); Es ist auch für direkte SQL-Abfragen sehr relevant.

Der Unterschied, den Sie sehen, besteht also darin, dass SQL Server zwei sehr unterschiedliche Ausführungspläne erstellt, da die beiden Abfragen unterschiedlich strukturiert sind.

Glücklicherweise ist die Lösung sehr einfach:Setzen Sie die Parameter in interne Variablen und verwenden Sie diese stattdessen in Ihrer Abfrage. Ich habe dies mit meinem Bericht gemacht und der Produktionsbericht ging auf 10 Sekunden zurück, wie es die Entwicklungsversion in Visual Studio tat.

Um das Parameter-Sniffing für Ihre erste Abfrage zu umgehen, würden Sie es so aussehen lassen:

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;