CAST(DATETIME AS DATE) over WHERE clause

后端 未结 3 589
心在旅途
心在旅途 2021-01-06 12:31

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) = \'         


        
相关标签:
3条回答
  • 2021-01-06 13:02

    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)
    

    enter image description here

    enter image description here

    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)
    

    enter image description here

    enter image description here

    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)
    

    enter image description here

    enter image description here

    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);
    

    enter image description here

    enter image description here

    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);
    

    enter image description here

    enter image description here

    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

    0 讨论(0)
  • 2021-01-06 13:15

    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

    0 讨论(0)
  • 2021-01-06 13:22

    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.

    0 讨论(0)
提交回复
热议问题