Help with datetime sql

  • I have a datetime column with an example value of: 1/1/2008 10:30:59

    I would like to filter on the dates of January and February of 2007, with the time being between 8PM and 9PM. What would the SQL be?

    Thanks...Nali

  • I assume this is what you want

    Select col1,col2... from table1 where col2 between 2007/01/01 12:00:00:000' AND '2007/02/28 23:59:59:997' .

    "Keep Trying"

  • Not quite, I only want the values between 8PM and 9PM for all of January and February...

  • I think you need something like this

    select * from tablename where datepart(yy , dateColumn) = 2007

    and datepart(mm,dateColumn) <= 2

    and datepart(hh, dateColumn) between 20 and 21

    🙂

    :w00t:

  • though i liked the solution provided in the previous post, here is another version..

    select * from mytable where convert(varchar(6),datecolumn,112) between '200701' and '200702'

    and convert(varchar(2), datecolumn, 108) between '20' and '21'



    Pradeep Singh

  • When you use function in the where clause on the column, the server can’t use any index that was created on that column. Although the queries that were introduced by other will work, they will not use an index on DateColumn (assuming that one exists and can improve the performance). In order to have a chance that an index will be used, I would use the dates in the query instead of using functions that will give me parts of the dates. My version of the query will be:

    select * from tablename

    where dateColumn >= '20070101' and dateColumn < '20070301'

    and datepart(hh, dateColumn) = 20

    Adi

    --------------------------------------------------------------
    To know how to ask questions and increase the chances of getting asnwers:
    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    For better answers on performance questions, click on the following...
    http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • Adi Cohn (1/2/2009)


    When you use function in the where clause on the column, the server can’t use any index that was created on that column. Although the queries that were introduced by other will work, they will not use an index on DateColumn (assuming that one exists and can improve the performance). In order to have a chance that an index will be used, I would use the dates in the query instead of using functions that will give me parts of the dates. My version of the query will be:

    select * from tablename

    where dateColumn >= '20070101' and dateColumn < '20070301'

    and datepart(hh, dateColumn) = 20

    Adi

    It's always nice to have some proof...and here it is...

    [font="Courier New"]-- Some code nicked from Jeff Moden's excellent tally table article

    -- to make up sample dates

    --===== Presets

    DECLARE @DateStart DATETIME

    DECLARE @DateEnd   DATETIME

    SELECT @DateStart = '1998-01-01 06:00',        

        @DateEnd   = DATEADD(yy,15,@DateStart)

    --===== Display the shift number and date/times

    DROP TABLE #Dates

    SELECT DATEADD(mi,10*(t.number  ),@DateStart) AS ShiftEnd  

    INTO #Dates -- (788977 row(s) affected)

    FROM dbo.Numbers t  

    WHERE DATEADD(mi,10*(t.number-1),@DateStart) <= @DateEnd

    -- run with / without index for comparison

    CREATE  INDEX [ShiftEnd] ON #Dates([ShiftEnd]) ON [PRIMARY]

    ------------------------------------------------------------------------

    DECLARE @DateRangeStart DATETIME, @DateRangeEnd DATETIME

    SET @DateRangeStart = '2007-01-01 00:00'

    SET @DateRangeEnd = '2007-03-01 00:00'

    SET STATISTICS TIME ON

    SET STATISTICS io ON

    ---------------------------------------------------------------------

    -- Adi's version

    SELECT COUNT(*)

    FROM #Dates

    WHERE ShiftEnd >= @DateRangeStart AND ShiftEnd < @DateRangeEnd

        AND DATEPART(hh, ShiftEnd) = 20

    -- 354 rows

    -- WITHOUT INDEX CPU time = 156 ms,  elapsed time = 167 ms.

    -- WITH INDEX CPU time = 0 ms,  elapsed time = 2 ms. logical reads 26

    ---------------------------------------------------------------------

    -- vishal

    SELECT COUNT(*)

    FROM #Dates

    WHERE DATEPART(yy , ShiftEnd) = 2007

    AND DATEPART(mm,ShiftEnd) <= 2

    AND DATEPART(hh, ShiftEnd) BETWEEN 20 AND 21

    -- (708 rows)

    -- WITHOUT INDEX CPU time = 214 ms,  elapsed time = 214 ms.

    -- WITH INDEX CPU time = 213 ms,  elapsed time = 213 ms. logical reads 2153

    -- vishal corrected

    SELECT COUNT(*)

    FROM #Dates

    WHERE DATEPART(yy , ShiftEnd) = 2007

    AND DATEPART(mm,ShiftEnd) <= 2

    AND DATEPART(hh, ShiftEnd) = 20

    -- (354 rows)

    -- WITHOUT INDEX CPU time = 212 ms,  elapsed time = 212 ms.

    -- WITH INDEX CPU time = 219 ms,  elapsed time = 224 ms. logical reads 2153

    ---------------------------------------------------------------------------

    -- ps

    SELECT COUNT(*)

    FROM #Dates

    WHERE CONVERT(VARCHAR(6),ShiftEnd,112) BETWEEN '200701' AND '200702'

    AND CONVERT(VARCHAR(2), ShiftEnd, 108) BETWEEN '20' AND '21'

    -- (708 rows)

    -- WITHOUT INDEX CPU time = 2172 ms,  elapsed time = 2203 ms.

    -- WITH INDEX CPU time = 2578 ms,  elapsed time = 2583 ms. logical reads 2153

    -- ps corrected

    SELECT COUNT(*)

    FROM #Dates

    WHERE CONVERT(VARCHAR(6),ShiftEnd,112) BETWEEN '200701' AND '200702'

    AND CONVERT(VARCHAR(2), ShiftEnd, 108) = '20'  

    -- (354 rows)

    -- WITHOUT INDEX CPU time = 2172 ms,  elapsed time = 2172 ms.

    -- WITH INDEX CPU time = 1453 ms,  elapsed time = 1460 ms. logical reads 2153

    -----------------------------------------------------------------------------

    -- ChrisM version

    SELECT COUNT(*)

    FROM (SELECT *

        FROM #Dates

        WHERE ShiftEnd >= @DateRangeStart AND ShiftEnd < @DateRangeEnd ) d

    WHERE DATEPART(hh, ShiftEnd) = 20

    -- (354 rows)

    -- WITHOUT INDEX CPU time = 170 ms,  elapsed time = 170 ms.

    -- WITH INDEX CPU time = 0 ms,  elapsed time = 2 ms. logical reads 26

    ---------------------------------------------------------------------

    SET STATISTICS TIME OFF

    SET STATISTICS io OFF

    [/font]

    In the absence of an index, Adi's query will always be fastest because dates in the table are checked as-is against the reference dates - the other methods have to check the return value of the function against the reference. DATEPART or CONVERT is applied to every single date in the table.

    When the index is present, there's no contest. Adi's query is back at base with a cold beer while the others are figuring out which way to go through the dust.

    Cheers

    ChrisM

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Excellent way to prove the best performing query. After going through the explanation, one can easily make up his mind as how good queries need to be written.

    Thanks Chris for this 🙂



    Pradeep Singh

  • Thank you everyone, it was very helpful.

  • Chris Morris (1/2/2009)

    ...In the absence of an index, Adi's query will always be fastest because dates in the table are checked as-is against the reference dates - the other methods have to check the return value of the function against the reference. DATEPART or CONVERT is applied to every single date in the table...

    Great Analysis Chris!

    Just a couple of points that are perhaps for my benefit than anything else ;

    i) with Adi's solution, will the DATEPART not be evaluated in any case ? You cannot assume SQL will short circuit...

    ii) Will it really be checked "as-is" ? Is there not some conversion still required as the parameters are strings ?

    iii) I presume the procedure and data caches were cleared in your testing ?

    iv) Would it not be marginally quicker if Adi's query was refined as ;

    select * from tablename

    where dateColumn >= '20070101 20:00:00' and dateColumn < '20070301 21:00:00' (as Chirag's query)

    thereby no functions are necessary ?

    Please note this is not a criticism, I think your post is great, keep up the good work! My comments are merely to increase our understanding.

    Regards

    Preet

  • Hi Preet

    Discussion is always good. Taking your points one by one:

    i) with Adi's solution, will the DATEPART not be evaluated in any case ? You cannot assume SQL will short circuit...

    in this case it's only evaluated on the relevant date range. You can't assume SQL will short circuit, you must test. My version of the query enforces the filters in two separate steps, date range first followed by time filter - I use this quite a lot, it ain't perfect but it usually works and as you can see it usually has little cost.

    ii) Will it really be checked "as-is" ? Is there not some conversion still required as the parameters are strings ?

    Now, why would you want to do that when it's so easy to convert the strings to datetimes?

    iii) I presume the procedure and data caches were cleared in your testing ?

    I ran the bunch of queries about 8 or 10 times with & without index, with the sample data generation part commented out. There's some variation in the timings which you will see if you try it for yourself, but those variations are much smaller than the differences between the methods.

    iv) Would it not be marginally quicker if Adi's query was refined as ;

    select * from tablename

    where dateColumn >= '20070101 20:00:00' and dateColumn < '20070301 21:00:00' (as Chirag's query)

    Nah, this doesn't work - it returns all datetimes in the date range, OP only wants datetimes in the date range where the time component is between 8pm and 9pm. Quicker doesn't count if the results aren't what you want.

    Cheers

    ChrisM

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Chris Morris (1/2/2009)


    It's always nice to have some proof...and here it is...

    Very, [font="Arial Black"]very [/font]well done... there's absolutely no argument when the arguments offered are in the form of viable, repeatable, demonstrable, working code. I wish more people would follow that example.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • Thanks Jeff :blush:

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

Viewing 13 posts - 1 through 12 (of 12 total)

You must be logged in to reply to this topic. Login to reply