简体   繁体   中英

What is the difference between these SQL statements

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM