Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Perché la seconda query T-SQL viene eseguita molto più velocemente della prima quando viene chiamata da Reporting Services 2005 in un'app Web

Potresti esserti imbattuto in una query che ha un problema con lo sniffing dei parametri, che ha a che fare con il modo in cui Sql Server tenta di ottimizzare il tuo piano di esecuzione delle query, ma nei casi in cui è coinvolto Reporting Services lo incasina completamente e lo fa funzionare incredibilmente lentamente.

Ho avuto un caso con un rapporto che conteneva due query complesse di circa 150 righe ciascuna ma che sono state eseguite in 7 secondi nel mio ambiente di sviluppo:l'intero rapporto ha richiesto meno di 10 secondi. Tuttavia, quando distribuito nel server SSRS di produzione, il report ha richiesto più di 7 minuti e spesso è scaduto rendendo il report non eseguibile.

La maggior parte delle informazioni su questo problema ne parla in relazione alle stored procedure. Non respingere questo perché non stai utilizzando stored procedure (come ho fatto per molto tempo); è molto rilevante anche per le query SQL semplici.

Quindi la differenza che stai vedendo è che Sql Server sta creando due piani di esecuzione molto diversi poiché le due query sono strutturate in modo diverso.

Fortunatamente, la soluzione è molto semplice:inserisci i parametri nelle variabili interne e usali invece nella tua query. L'ho fatto con il mio report e il report di produzione è tornato a 10 secondi come faceva la versione di sviluppo in Visual Studio.

Per ignorare lo sniffing dei parametri per la tua prima query, dovresti farlo assomigliare a questo:

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;