Search code examples
sqlsql-serverdatetimefloor

Floor() function on a datetime - SQL Server


http://sqlfiddle.com/#!18/639ec/2

CREATE TABLE TEST
(
ID INT,
OrderNo int,
DateReceived datetime
)

INSERT INTO TEST (ID,OrderNo,DateReceived)
VALUES ('1', '3454', '07-20-2018 00:00:00')

Query:

DECLARE @StartDate datetime,
@EndDate datetime,
@FlooredStart datetime ,
@FlooredEnd datetime 

SET @StartDate = '07-20-18'
SET @EndDate = '07-20-18'
SET @FlooredStart  = CAST(FLOOR(CAST(@startDate AS FLOAT)) AS DATETIME)
SET @FlooredEnd = DATEADD(d, 1, CAST(FLOOR(CAST(@endDate AS FLOAT)) AS DATETIME))

SELECT * FROM TEST 
WHERE DateReceived = @FlooredStart and DateReceived < @FlooredEnd

In my live version if the date received is left blank it defaults to the time 12:00:00AM of that day. So for this example if i search for orders on the 07-20-18 it wont return the order that was placed at 12:00:00AM of the 07-20-18.

So i figure to add >= to the clause

DECLARE @StartDate datetime,
@EndDate datetime,
@FlooredStart datetime ,
@FlooredEnd datetime 

SET @StartDate = '07-20-18'
SET @EndDate = '07-20-18'
SET @FlooredStart  = CAST(FLOOR(CAST(@startDate AS FLOAT)) AS DATETIME)
SET @FlooredEnd = DATEADD(d, 1, CAST(FLOOR(CAST(@endDate AS FLOAT)) AS 
DATETIME))

SELECT * FROM TEST 
-- WHERE DateReceived BETWEEN @StartDate AND @EndDate
WHERE DateReceived >= @FlooredStart and DateReceived < @FlooredEnd

Result:

| ID | OrderNo |         DateReceived |
|----|---------|----------------------|
|  1 |    3454 | 2018-07-20T00:00:00Z |

I was just wondering if i had this logic correct? And can anyone explain to me exactly what the floor() function is doing. I understand that its calculating the first day of year/month but is that needed here? I looked online and i could'nt find a definitive answer anywhere.

Thanks


Solution

  • Instead of using floor to "truncate" the time part, use the date-only date type :

    DECLARE @StartDate date = '20180720', @EndDate date='20180720'
    
    SELECT * FROM TEST 
    WHERE cast(DateReceived date) between @startdate and @enddate
    

    or, for a single date only :

    SELECT * FROM TEST 
    WHERE cast(DateReceived date) = @startdate 
    

    Note that I'm using the unseparated date literal. This is the only unambiguous date format. The other unambiguous format is the full ISO8601 format for the datetime types. Two-digit years are just begging for incorrect interpretation way.

    cast(DateReceived date) will convert the datetime values in DateReceived into date values. Usually, it's a bad idea to apply functions to fields because it prevents the query optimizer to use any indexes. SQL Server understands cast( ... as date) though and converts :

    cast(DateReceived date) = @startdate 
    

    To a range query equivalent to DateReceived between @startdate at 00:00 but before the next day at 00:00 , allowing it to use any indexes on DateReceived