1

I have a webform where a student can login, on that form I will have to display the TOP 3 students along with their total score and id's. Also the total score of current logged in student and his total score

For getting this information I have a stored procedure which takes @StudentId as the input parameter and returns the TOP 3 students and their total score and also the logged in students total score and his rank. So the total number of rows returned by my query will be 4 and if in case the Logged in student is among TOP 3 students then the total number of rows returned will be 3

Below is the query for that, I am not posting how the data will be populated into the @StudentScore table as that's a very simple query with some If conditions and a simple join.

DECLARE @StudentScore AS TABLE (StudentId INT, [Score] INT)
DECLARE @StudentRankTable AS TABLE (StudentId INT, [TotalScore] INT, [StudentRank] INT)


INSERT INTO @StudentRankTable 
    SELECT 
        [StudentId], 
        SUM([Score]) AS [TotalScore], 
        DENSE_RANK () OVER (ORDER BY SUM([Score]) DESC) [Rank]
    FROM @StudentScore
    GROUP BY [StudentId] ORDER BY [Rank]


SELECT TOP 3 
    [StudentId], [TotalScore], [StudentRank] 
FROM StudentRankTable  

UNION

SELECT 
    [StudentId], [TotalScore], [StudentRank] 
FROM @StudentRankTable 
WHERE [StudentId] = @StudentId 
ORDER BY [StudentRank]

Below is how the sample data in @StudentScore temp table looks like.

StudentId Score
   101      21
   103      43
   101      11
   107      5
   103      7

The query works as expected and i am getting the desired output.

Problem: I am getting exception very frequently messages saying that this query has timed out, but when i try running this query on SQL Server the query returns the output in 3-4 seconds. I really cant understand where the problem is lying as when i run the query with different @StudentId's i get the output in 3-4 and still i see of exception messages with the below message

Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding

any help and suggestions on this issue?

Note - This is not happening each and every time when student logs in, but many times this is happening.

Chay
  • 11
  • 2
  • Your issue is probably Parameter Sniffing.how large is the table? Do you have query store enabled? – Stu Apr 01 '21 at 19:20
  • also, looking at your query you are using `table variables`. Try replacing it with a standard `#temptable` – Stu Apr 01 '21 at 19:22
  • or use a view instead of a table variable – Hogan Apr 01 '21 at 19:24
  • @Stu , I am not aware of what store enabled means, how large is the table --- which table do u mean ? – Chay Apr 01 '21 at 19:27
  • [Query store](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store?view=sql-server-ver15) - how large is the table you are populating the @tableVariables from? How many rows are you processing? – Stu Apr 01 '21 at 19:30
  • @Stu There are 3-4 lakhs of of records from which i am getting the data.. when i looked at the count of student records the cte Cte_StudentScore is holding it was around 2 lakhs.. Only confusing thing is even when i am changing the parameter and runinng the query it is returning me the output in 4 seconds. – Chay Apr 01 '21 at 19:39
  • So I googled lakhs and turns out it's 100,000, so that's quite a lot. you probably have parameter sniffing going on, where the cached execution plan is optimal for some parameters but not others. Try adding a `recompile` hint and see if that helps. And replace the table variables with temp tables. – Stu Apr 01 '21 at 19:45
  • @Stu will try with your suggestions, when you say temp tables i am assuming it as local temp tables.. when we compare temp tables and views which one would you suggest in my case temp table or view ? – Chay Apr 01 '21 at 19:51
  • The answer is - as is normally the case - it depends. Hard to say without a lot more info and your actual execution plan. However with many 1000s of rows SqlServer will not be getting accurate statistcs on the table variables; it will with a #temp table. – Stu Apr 01 '21 at 19:52
  • This query seems a bit strange: you are calculating `DENSE_RANK`, which returns tied results `1,2,2,2,3,4,4,4` then you are getting `ROW_NUMBER` partitioned over that, and returning only #1. So in that example you would get rows 1,2,5 which are numbered `1,2,3` respectively, but in completely arbitrary order – Charlieface Apr 01 '21 at 20:01
  • @Charlieface yeah, the reason is i just need TOP 3 scores, i am not much bothered about the Students who got that score...if i don't use partition and take #1, if there are more than 1 Student who got same score then in the TOP 3 scores there could be duplicates. – Chay Apr 01 '21 at 20:05
  • Quite often when everything runs perfectly in SSMS, but doesn't in the real application is because you get a different execution plan for SSMS because you have different session settings, and the procedure gets compiled with the parameters you test with in SSMS. In the application the procedure gets the plan in the first execution (=parameter sniffing). You can ask it to be re-done by running `sp_recompile procname` -- and if that's the reason, the next execution from the application should work perfectly again. – James Z Apr 01 '21 at 20:08
  • So we actually want the 3 top score *value* as opposed to the 3 top *students* that scored? – Charlieface Apr 01 '21 at 20:09
  • If the problem is parameter sniffing, there's a lot of material about it available. But you probably need to learn first how to look into your plans and performance statistics in plan cache. – James Z Apr 01 '21 at 20:11
  • @Charlieface Thats true, i just need top 3 score values and obviously they will be ranked TOP 1,2,3 .. along with that i will need current Student Rank and his total score – Chay Apr 01 '21 at 20:27
  • @JamesZ I have to do some research on the areas you suggested, your suggestions will help me to research in the right direction.. Thank you for that – Chay Apr 01 '21 at 20:29
  • 200k is not actually that big if you are using indexes -- are you sure you defined the indexes correctly? – Hogan Apr 01 '21 at 21:15
  • Hogan - 200k records in the CTE , the main tables can have around 400k records... the CTE and the table variable dont have any Indexes but the actual main tables which are used to populate @StudentScore have the indexes. – Chay Apr 02 '21 at 03:50

1 Answers1

0

Maybe in SSMS you can view the result of your query or stored procedure, but your code doesn't return a query or table, I think that you should create a function that returns a table to get what you need.

You can create views to replace your temporary tables, and can create a function to get the result that you want from anywhere, an API o what you want, try this

create view v_studentRank as (
 SELECT [StudentId], 
  SUM([Score]) AS [TotalScore], 
  DENSE_RANK () OVER (ORDER BY SUM([Score]) DESC) [Rank]
 FROM StudentScore
 GROUP BY [StudentId]
);
go

create function f_studentRank(
 @StudentId int)
returns table
as
return
(
 SELECT TOP(3) [StudentId], [TotalScore], [Rank] 
 FROM (
  SELECT *, 
   ROW_NUMBER() OVER (ORDER BY [Rank]) AS [RowNumber]
  FROM v_studentRank) as v
 WHERE [RowNumber] <= 3
 UNION
 SELECT [StudentId], [TotalScore], [Rank] 
 FROM v_StudentRank 
 WHERE [StudentId] = @StudentId 
);
go

select *
from f_studentRank(4)
order by [Rank]

You can find more information about here

  • 1
    I can't begin to imagine why a function would speed this up, other than forcing the whole query to be written without the use of a table variable, which may be faster or slower, but not relevant to what you are saying – Charlieface Apr 01 '21 at 19:41
  • You can create views to replace your temporary tables, and can create a function to get the result that you want from anywhere, an API o what you want, try this – Andrés Aldana Apr 01 '21 at 21:44
  • You could just as well do it with a CTE, and I still don't get why this would improve performance – Charlieface Apr 01 '21 at 21:47