Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a T-SQL variable comparison slower than GETDATE() function-based comparison?

I have a T-SQL statement that I am running against a table with many rows. I am seeing some strange behavior. Comparing a DateTime column against a precalculated value is slower than comparing each row against a calculation based on the GETDATE() function.

The following SQL takes 8 secs:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
DECLARE @TimeZoneOffset int = -(DATEPART("HH", GETUTCDATE() - GETDATE()))
DECLARE @LowerTime DATETIME = DATEADD("HH", ABS(@TimeZoneOffset), CONVERT(VARCHAR, GETDATE(), 101) + ' 17:00:00')
SELECT TOP 200 Id, EventDate, Message 
FROM Events WITH (NOLOCK)
WHERE EventDate > @LowerTime
GO

This alternate strangely returns instantly:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
SELECT TOP 200 Id, EventDate, Message 
FROM Events WITH (NOLOCK)
WHERE EventDate > GETDATE()-1
GO

Why is the second query so much faster?

EDITED: I updated the SQL to accurately reflect other settings I am using

like image 348
jedatu Avatar asked Jul 01 '11 03:07

jedatu


People also ask

What are the advantages of using variables in TSQL?

Advantages of table variables. Table variables don't require locking and logging resources, nor do they have to be stored in a database (although see hinit below). For this reason, they will run more quickly than temporary tables.

Is datediff slow?

The reason DATEDIFF() runs slow is that using it takes a bit of time to perform the calculation, the query optimizer is (probably) ending up running it for the entire table, and there is (probably) no index to help it select the required rows.

How do I get data greater than the current date in SQL?

In this article, we will see the SQL query to check if DATE is greater than today's date by comparing date with today's date using the GETDATE() function. This function in SQL Server is used to return the present date and time of the database system in a 'YYYY-MM-DD hh:mm: ss. mmm' pattern.

Does Getdate () include time?

GETDATE returns the current date and time in the current session time zone (UTC by default). It returns the start date or time of the current statement, even when it is within a transaction block.


1 Answers

After doing a lot of reading and researching, I've discovered the issue here is parameter sniffing. Sql Server attempts to determine how best to use indexes based on the where clause, but in this case it isnt doing a very good job.

See the examples below :

Slow version:

declare @dNow DateTime  
Select @dNow=GetDate()  
Select *  
From response_master_Incident rmi  
Where rmi.response_date between DateAdd(hh,-2,@dNow) AND @dNow  

Fast version:

Select *  
From response_master_Incident rmi  
Where rmi.response_date between DateAdd(hh,-2,GetDate()) AND GetDate()  

The "Fast" version runs around 10x faster than the slow version. The Response_Date field is indexed and is a DateTime type.

The solution is to tell Sql Server how best to optimise the query. Modifying the example as follows to include the OPTIMIZE option resulted in it using the same execution plan as the "Fast Version". The OPTMIZE option here explicitly tells sql server to treat the local @dNow variable as a date (as if declaring it as DateTime wasnt enough :s )

Care should be taken when doing this however because in more complicated WHERE clauses you could end up making the query perform worse than Sql Server's own optimisations.

declare @dNow DateTime

SET @dNow=GetDate()

Select ID, response_date, call_back_phone 
from response_master_Incident rmi
where rmi.response_date between DateAdd(hh,-2,@dNow) AND @dNow

-- The optimizer does not know too much about the variable so assumes to should perform a clusterd index scann (on the clustered index ID) - this is slow

-- This hint tells the optimzer that the variable is indeed a datetime in this format (why it does not know that already who knows)
OPTION(OPTIMIZE FOR (@dNow = '99991231'));
like image 198
mikecamimo Avatar answered Oct 16 '22 06:10

mikecamimo