Reputation: 39
This statement works:
DECLARE @variable_name INT
SET @variable_name = (SELECT DATEPART(DAYOFYEAR, GETDATE()))
SELECT *
FROM artists
WHERE SUBSTRING(DOB, 0, 5) > '1900'
AND CONVERT(INT, DATEPART(DAYOFYEAR, DOB)) = @variable_name
This one doesn't work:
SELECT *
FROM artists
WHERE SUBSTRING(DOB, 0, 5) > '1900'
AND CONVERT(INT, DATEPART(DAYOFYEAR, DOB)) =
(SELECT CONVERT(INT, datepart(DAYOFYEAR, getdate())))
it returns the error
Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
This one works
SELECT *
FROM artists
WHERE DOB IS NOT NULL
AND SUBSTRING(DOB, 0, 5) > '1900'
AND convert(int, DATEPART(DAYOFYEAR, DOB)) = '100'
Today happens to be the 100th day of the year
and I don't understand why, I am using SQL Server 2008.
The column is set as varchar(10)
. I have dates in there going back to the 1700's
Date format in the table is
1930-04-10
YYYY-MM-DD
I can put both queries in the same window, highlight and run one at a time against the same data set. And get the above results.
Upvotes: 0
Views: 115
Reputation: 94914
In both queries you are checking two conditions:
SUBSTRING(DOB, 0, 5) > '1900'
and
CONVERT(INT, DATEPART(DAYOFYEAR, DOB)) = some integer;
The DBMS decides which one to check first. Obviously in your first query, the DBMS checks the first expression first, thus ruling out some DOB
that couldn't be converted with DATEPART
. But the second query checks the second condition first and fails trying to convert some value.
My advice: Your best bet would be to change your table and have the DOB stored as DATETIME2
rather than VARCHAR(10)
.
If this cannot be done for some reason, You can force precedence of the first condition over the second by using a subquery (derived table):
select *
from artists
from
(
select *
from artists
where substring(dob, 1, 4) > '1900'
) after_1900
where datepart(dayofyear, dob) = datepart(dayofyear, getdate());
This works (hopefully), but isn't completely proper. For example a DOB
can still contain an invalid date, such as '2016-XY-??' or simply '2016-02-30'. And in the DATEPART
expression you treat DOB
as a DATETIME
which it is not. Implicit conversion takes place, which can easily fail.
Here is a query with a check for the right date format first and an explicit string to date conversion:
select dob, name
from
(
select convert(datetime2, dob + ' 00:00:00', 120) as dob, name
from artists
from
(
select dob, name
from artists
where dob like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
) valid_data_strings
) converted
where datepart(year, dob) > 1900
and datepart(dayofyear, dob) = datepart(dayofyear, getdate());
Still, this doesn't solve the 30th February problem, though. I don't think there is an easy way to detect valid date strings and only consider these records then. Possible, yes, but certainly some work with all that leap year logic needed.
As mentioned, it would be much easier to change the table design instead.
Upvotes: 1
Reputation: 39
The answer is simple, but still strange.
This statement doesn't work
SELECT * FROM artists WHERE SUBSTRING(DOB, 0, 5) > '1900'
AND CONVERT(INT, DATEPART(DAYOFYEAR, DOB)) = (SELECT CONVERT(INT, datepart(DAYOFYEAR, getdate())))
This one does
SELECT * FROM artists WHERE SUBSTRING(DOB, 0, 5) > '1900'
AND CONVERT(INT, DATEPART(DAYOFYEAR, DOB)) = (CONVERT(INT, datepart(DAYOFYEAR, getdate())))
by removing the Select from the get current date section, the statement springs to life, this can only mean that the addition of the "Select" (which actually makes no difference), changes the order of precedence, which makes sense in a convoluted way. But I would argue is a bug.
Upvotes: 0