4

Trick to Optimize TOP clause in SQL Server

 3 years ago
source link: https://www.mssqltips.com/sqlservertip/2053/trick-to-optimize-top-clause-in-sql-server/?utm_campaign=Feed%3A+MSSQLTips-LatestSqlServerTips+%28MSSQLTips+-+Latest+SQL+Server+Tips%29
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Trick to Optimize TOP clause in SQL Server

By: Matteo Lorini   |   Updated: 2021-04-01   |   Comments (8)   |   Related: More > Performance Tuning

Problem

Have you ever come across a situation where a SELECT query with a TOP clause will perform well most of the time, but as soon as you change the TOP value the same query is 10 to 20 times slower?  In this tip I will show you why this may happen and ways to prevent this.

Solution

The TOP clause specifies that only the first set of rows will be returned from the query result. In this tip I will show how to make the query results predictable by avoiding "Sort Warning".

Let's create our sample table.

--Source code provided by: www.sqlworkshops.com
SET NOCOUNT ON
CREATE TABLE tab7 (c1 INT PRIMARY KEY CLUSTERED, c2 INT, c3 CHAR(2000))
GO

BEGIN TRAN
GO

DECLARE @i INT
SET @i=1
WHILE @i<=50000
BEGIN
   INSERT INTO tab7 VALUES (@i,RAND()*200000,'a')
   SET @i=@i+1
END 
COMMIT TRAN
GO	

Let's update the statistics with a full scan to make the optimizer work easier.

UPDATE STATISTICS tab7 WITH fullscan
GO

Test 1

Let's set statistics time on and execute the following query.

SET STATISTICS time ON
GO
--Source code provided by: www.sqlworkshops.com
SELECT num_of_reads, num_of_bytes_read,
num_of_writes, num_of_bytes_written
FROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1) 
GO

SELECT TOP 100 c1, c2,c3
FROM tab7
WHERE c1<30000
ORDER BY c2
GO

SELECT num_of_reads, num_of_bytes_read,
num_of_writes, num_of_bytes_written
FROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
GO
CPU time = 124 ms,  elapsed time = 91 ms

Before Query execution
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
725864               46824931328          793589               51814416384

After Query execution
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
725864               46824931328          793589               51814416384

The query ran fast (91ms).

The number of reads and writes on tempdb before and after the execution of our query are the same. This means that our query was able to complete the sort in memory without spilling to tempdb.

Test 2

Now, lets execute the following query. Please note the new value in the TOP clause which was changed from 100 to 101.

SELECT num_of_reads, num_of_bytes_read,num_of_writes, num_of_bytes_written
FROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1) 
GO

SELECT TOP 101 c1, c2, c3
FROM tab7
WHERE c1<30000
ORDER BY c2
GO

SELECT num_of_reads, num_of_bytes_read,num_of_writes, num_of_bytes_written
FROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
GO
CPU time = 376 ms,  elapsed time = 726 ms

Before Query execution
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
725864               46824931328          793589               51814416384

After Query execution 
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
726880               46881570816          795356               51928072192

The query runs much slower (726 ms).

The sort operation spilled over to tempdb, which we can see by the read/write values before and after execution of our query have increased.

Ways to Fix the Problem

If you are running SQL 2008 or later 64bit, the work around to make the sort happen in memory is to change the query, so the optimizer can allocate more memory allowing the sort operation to take place in memory as shown below. Please see my previous tip for further explanation.

SELECT TOP 101 c1, c2, CONVERT(VARCHAR(4500),c3)
FROM tab7
WHERE c1<30000
ORDER BY c2

If If you are running SQL 2005 or later TOP (@variable) does the trick.

SELECT num_of_reads, num_of_bytes_read, num_of_writes, num_of_bytes_writtenROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
GO

DECLARE @i INT
SET @i=101
SELECT TOP(@i) c1, c2, CONVERT(VARCHAR(5000),c3)
FROM tab7
WHERE c1<30000
ORDER BY c2
GO

SELECT num_of_reads, num_of_bytes_read,num_of_writes, num_of_bytes_written
FROM sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
GO	
CPU time = 267 ms,  elapsed time = 124 ms

Before Query execution
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
729075               47004737536          799045               52165402624

After Query execution
num_of_reads         num_of_bytes_read    num_of_writes        num_of_bytes_written
-------------------- -------------------- -------------------- --------------------
729075               47004737536          799045               52165402624

As we can see, the query is fast again and there is no activity on tempdb.

Next Steps
Last Updated: 2021-04-01
About the author

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK