Conversion of a varchar data type to a datetime data type resulted in an out-of-range value

I checked your profile and saw that you are in the UK. If your sql server is set to use the dateformat dmy then that explains your issue. Without using the 'T' instead of the space in the datetime string, Sql Server won't recognize it as ISO8601 format.

Try this:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');

Querying using dates and/or datetimes can be tricky, to make sure you are getting what you are looking for I recommend reading:

  • Aaron Bertrand's Bad habits to kick : mis-handling date / range queries
  • Robyn Page's SQL Server DATE/TIME Workbench

edit: to clarify the out of range value in your error message would be from interpreting the month as 30 and the day as 11.


I do not understand why the data is being converted from varchar to datetime when 'Created' is set to datetime

The literals you are providing for comparison to the Created column are strings. To compare those literals with the datetime column, SQL Server attempts to convert the strings to datetime types, according to the rules of data type precedence. Without explicit information about the format of the strings, SQL Server follows its convoluted rules for interpreting strings as datetimes.

In my view, the neatest way to avoid these types of issues is to be explicit about types. SQL Server provides the CAST and CONVERT functions for this purpose. When working with strings and date/time types, CONVERT is to be preferred because it provides a style parameter to explicitly define the string format.

The question uses strings in ODBC canonical (with milliseconds) format (style 121). Being explicit about the data type and string style results in the following:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);

That said, there are good reasons (as Aaron points out in his answer) to use a half-open range instead of BETWEEN (I use style 120 below just for variety):

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);

Being explicit about types is a very good habit to get into, particularly when dealing with dates and times.


Since BETWEEN is very problematic due to rounding of different date/time types and other problems, and since YYYY-MM-DD is not a safe format without the awkward T, an open-ended range using ISO standard full dates with no separators is a much better approach:

WHERE Created >= '20141101' AND Created < '20141201';