views:

186

answers:

3

I have a query that has a very costly INDEX SEEK operation in the execution plan. In order to track down the cause i set IO STATISTICS on and ran it. In the problem section it gave the following statistics:

Table '#TempStudents_Enrollment2_____________________________________000000004D5F'. Scan count 0, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#TempRace2______________________________________________000000004D58'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'RefRace'. Scan count 120, logical reads 240, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'RefFedEnctyRaceCatg'. Scan count 18, logical reads 36, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#43B0BA0F'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#42BC95D6'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#41C8719D'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#40D44D64'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#LEA2_________________________________________________000000004D56'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#39332B9C'. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#School2________________________________________________000000004D57'. Scan count 1, logical reads 29164, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#GenderKey______________________________________________000000004D5A'. Scan count 1, logical reads 29164, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#LangAcqKey_____________________________________________000000004D5B'. Scan count 1, logical reads 29164, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#TransferCatKey___________________________________________000000004D5C'. Scan count 1, logical reads 29164, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#ResCatKey______________________________________________000000004D5D'. Scan count 1, logical reads 29164, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'RPT_SnapShot_1_4_StuPgm_Denorm'. Scan count 2344954, logical reads 4992518, physical reads 16, read-ahead reads 8, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#3FE0292B'. Scan count 1, logical reads 2344954, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'RPT_SnapShot_1_4_StuEnrlmt_Denorm'. Scan count 20, logical reads 87679, physical reads 0, read-ahead reads 87425, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table '#GradeKey_______________________________________________000000004D59'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

What should I look for in here when i'm looking to improve the performance? The line with over 2 million for the Scan count looked suspicious to me but I really don't know. Does anyone see anything here that i should look into in more detail?

+1  A: 

It seems there is a rather costly index scan going on there: Table 'RPT_SnapShot_1_4_StuPgm_Denorm'. Scan count 2344954, logical reads 4992518.

Pent Ploompuu
I thought so too. From what I can tell this section in the execution plan comes back as an index seek that takes a very long time. Could these be related?
Abe Miessler
It might be an index seek in an inner loop join, so if you could change it to a hash-match join (or even better - merge join) then it might go a lot faster.
Pent Ploompuu
I think you might be right, my execution plan shows 27% being spend on the nested loop that preceeds my Index Seek operation (which takes up 70%). How would I change it to a hash match or merge join? Better indexes?
Abe Miessler
While doing some investigation i found the following quote: "The fastest JOIN method is the Loop JOIN, followed by the Merge and the Hash JOIN." Is this true? If so is there some special case where i would want to use something else?
Abe Miessler
A merge join would need only two index scans/seeks in total instead of an index seek per every outer row. Better indexes might help and also restructuring the query - for example using a join to a group by instead of a subquery.
Pent Ploompuu
A: 

The only figure worth worrying about is "Logical Reads". Physical Reads will depend on how much data is currently cached, which could change every time the query is run.

Scan count is sometimes telling, but isn't really worth focusing on.

Edit: See some more discussion of these results in this post here. What I meant by "telling" is that the scan count can sometimes be a "flag" indicating that SQL is retrieving data from that table inefficiently. But when it comes to trying different versions of your query while you optimize, I pay much more attention to the improvement I can make in Logical Reads.

BradC
Interesting, could you elaborate on what you mean when you say that the "Scan count is sometimes telling..."?
Abe Miessler
A: 

You already executed "set statistics IO on". In "Query" menu turn on "Include Actual Execution Plan" and "Include Client Statistics". Run your query/procedure. In "messages" tab look for highest "Logical reads" number, memorize that table. In "Execution plan" tab, look for table you found in step before (usually has highest Cost percentage associated in the plan). If it is "Scan" (table scan or index scan), you are missing appropriate index, or appropriate index has no good statistics. If it is "Seek", then rows you are seeking are wide scattered in blocks. You have to bring them physically together by creating CLUSTERED index on column you seek. That is VERY efficient method. Not many people is aware of what clustered indexes are. Spend some time studying on them. By default, Sql server creates primary key that is clustered, and most people leave it that way. And in many cases it can lead to performance degradation. You need clustered index to physically group rows together by column(s) you build your clustered index on. You can have just one clustered index per table. Clustered index does not have to be unique, does not have to be PK, can consist of more than one column. You can rewrite the query, e.g. replace exists with IN and vice-versa, or replace exists with table join. There is no "fastest" method of join. If there would be one, all other types would be automatically converted to that fastest one. It depends on the data, available indexes, amount of ram etc.

Always measure, do not assume. Measuring is only truth. For how much you managed to cut down logical reads, that much you succeeded to optimize the query. Other optimisations would be database-wide level done by DBA (memory cache, parallel processes, storage system, examining wait events, etc).