CAST(DATETIME AS DATE) over WHERE clause

雨燕双飞 提交于 2019-12-19 04:27:30

问题


I'm using SQL Server 2012 and I would like to know if I write the sentence:

SELECT MyDateTimeColumn 
FROM MyTable
WHERE CAST(MyDateTimeColumn AS DATE) = '2014-07-09'

is a slower way to trim the time over DATETIME columns, I have searched but I can't find anything about this strict sentence and I don't know how to show that impresive statistics about time consuming cast/convert to probe it myself.


回答1:


In SQL 2008+, CAST(foo AS date) is sargable, along with a few other manipulations. Look at the execution plans in the sqlfiddle.

SQL Fiddle Demo




回答2:


So, lets use Anon's example. I changed it to have both a Primary Key on a row number and Non Clustered index on date.

Also, I choose to create random dates instead of a simple incremental update.

Here is the code below to create the test database.

-- Do not save in physical database
USE [tempdb]
GO

-- Drop table
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test2]') AND type in (N'U'))
DROP TABLE test2
GO

-- Create table
CREATE TABLE test2 (my_num int NOT NULL, my_dt datetime NOT NULL);
GO

-- Add data
INSERT test2
SELECT 
    TOP 100000 
    ROW_NUMBER() OVER(ORDER BY (SELECT 1)) as my_num,
    DATEADD(minute, RAND() * 500 * ROW_NUMBER() OVER(ORDER BY (SELECT 1)), '2000-01-01') as my_dt
FROM 
    master.dbo.spt_values t1, master.dbo.spt_values t2
GO

-- Add primary key
ALTER TABLE Test2 ADD CONSTRAINT pk_My_Num PRIMARY KEY (my_num);
GO

-- Add nc index
CREATE INDEX ix_My_Dt ON Test2 (my_dt);
GO

Now, lets take a look at each solution. The pros and cons.

I am going to use trace flags to look at the algebraic query parse tree as well as the query plan.

Solution 1: Index scan which is bad and needs to apply conversion to each date field.

-- Show output to message screen
DBCC TRACEON(3604)

-- 1 - Not sargable, applies conversion to each date field
SELECT count(*)
FROM Test2 
WHERE CONVERT(varchar(10), my_dt, 120) >= '2000-02-01' 
  AND CONVERT(varchar(10), my_dt, 120) < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607)

Solution 2: Index seek which is good and no conversion on date field. However, cultural specific date format.

-- 2 - Sargable
SELECT count(*)
FROM Test2 
WHERE my_dt >= '2000-02-01' AND my_dt < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607)

Solution 3: Index seek which is good and no conversion on date field. However, you have to convert date to integer.

-- 3 - Implicit conversion, still Sargable
SELECT COUNT(*) FROM Test2 
WHERE my_dt >= 36555 AND my_dt < 36556
OPTION (RECOMPILE, QUERYTRACEON 8607)

Solution 4: Index seek which is good and no conversion on date field. Date is in cultural (country) neutral format. Best solution!

-- 4 - Sargable
SELECT count(*)
FROM Test2 
WHERE my_dt >= '20000201' AND my_dt < '20000202'
OPTION (RECOMPILE, QUERYTRACEON 8607);

Solution 5: Index seek which is good. Applies conversion to each date field which is bad. Cultural specific date constant. Most complicated query plan.

-- 5 - Explicit conversion, still sargable
--     applies conversion to each date field
SELECT COUNT(*) 
FROM Test2 
WHERE CAST(my_dt AS date) >= '2000-02-01' 
AND CAST(my_dt AS date) < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607);

In summary, use solution 4 which takes advantage of the index and is not cultural specific.

Using cast() is not a good suggestion. It uses the index but extra time is spent on converting each index value during the comparison.

Note to self, make sure I explain in detail what I mean.

Here are some good reads on the topic!

References - All about dates.

http://karaszi.com/the-ultimate-guide-to-the-datetime-datatypes

Reference - Aaron's suggestions on date usage.

https://sqlblog.org/2009/10/16/bad-habits-to-kick-mis-handling-date-range-queries

What is SARGABLE.

http://en.wikipedia.org/wiki/Sargable




回答3:


Use this proven method to "zero out" the time component of a datetime:

select MyDateTimeColumn
from MyTable
where DATEADD(dd, DATEDIFF(dd, 0, MyDateTimeColumn), 0) = CONVERT(date, '07-09-2014', 110)

Avoid casting to different date types if there's a faster way (like the trick above), and definitely try to avoid using string literals for dates without wrapping them in a CONVERT() to ensure your string format will get interpreted correctly.

If you have performance concerns and want to force it to use an index, I would suggest adding a column that is of type date (fill it with the existing column's value minus the time part), and index/search on that, or create an indexed view that accomplishes the same thing.



来源:https://stackoverflow.com/questions/24659476/castdatetime-as-date-over-where-clause

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!