开发者

SQL Server: Function Evaluation Time

SQL Server 2005:

Query below returns no data ((0 row(s) affected)), OK.

But when Line #3 function call is enabled, the query fails!

Conversion failed when converting datetime from character string.

Coming from Oracle background - This is just unthinkable!

Please tell me what's going on. Am I开发者_StackOverflow doing it wrong ?

Doesn't SS evaluate function calls in SELECT list at the last step (like Oracle does - or any sensible programmer would do)?

select  
   1 as one
   --,DATEPART(yyyy,cast(inception_date27 as datetime)) as inception_yr
 from [DBO].[H5_PREMIUM_DETAIL] dtl,
      [DBO].[H5_POLICY_MASTER] polmst 
  where polmst.policy_no2 = cast(dtl.arch_master_policy_no41 as numeric)      
  and len(inception_date27) != 8

Here is a small clip I recorded


Note: I know the root SOURCE of the bad data (some rows have "0" instead of "20110704") that SS complains about here - the gist is why this failure arise where no data found from the JOIN.


Doesn't SS evaluate function calls in SELECT list at the last step (like Oracle does - or any sensible programmer would do) ?

No. (and - No).

The general rule in SQL is that there's a defined "order" in which the parts of the query should appear to be processed, but that different database systems are free to re-order those operations, provided the end result is the same.

However, there's still some room for ambiguity, and SQL Server seems more likely than most systems to push conversions earlier in the process than might otherwise be expected - it may be that, given the IO overhead of the operations, and the expense of performing the join operation (how big are the tables? What type of join has the optimizer selected?), that performing the conversion operation before the join occurs is practically free, so that's what the optimizer has chosen to do.

It would be nice if SQL Server could mark such conversion failures in the rows as they're processed, and only report at the end if the result set still contains a row with a conversion failure, but that's not how it currently works.

Pushing all of it into a CASE expression in the select clause would probably be enough (similar to niktrs answer):

SELECT
   /* Other columns */
   CASE
       WHEN inception_date27 LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
           THEN DATEPART(year,CONVERT(datetime,inception_date27))
   END


Maybe you should have LEN equal to 8 not len different to 8

Replace

len(inception_date27) != 8

With

len(inception_date27) = 8

An alternative way is to use case for the date, when not valid then return NULL

Eg.

DECLARE @mydate VARCHAR(8)

SET @mydate = '20100708'

SELECT CASE WHEN LEN(@mydate) = 8 THEN DATEPART(yyyy,CAST(@mydate AS datetime)) ELSE NULL END 
--WHERE LEN(@mydate) <> 8


Looking at your query you are actively saying len(inception_date27)!=8, so you probably expect no results to show up.

But, you also specify that you know there are values of '0' in there. Those are being returned because 0!=8. So the join does appear to return results.

Try replacing len(inception_date27)!=8 with (0=1) to see what happens then. That should definitely not return any results.


Without knowing the exact details of how how SQL-Server works internally to execute queries, my guess for such a behaviour is that it uses parallel processes or threads to execute it.

So, there may be 3 threads, one checking polmst.policy_no2 = cast(dtl.arch_master_policy_no41 as numeric), another checking len(inception_date27) != 8 while the main process is gathering the results and sending rows that match to the 3rd process that is doing the conversion DATEPART(yyyy,cast(inception_date27 as datetime)).

This would NOT result in the above error. But the appearence of both CAST() and LEN() in the above conditions forbid the use of indexes in the related fields. So, perhaps (that's my wild guess), the 1st process is slower than the 2nd and the main process is sending results from the 2nd thread to the 3rd, just in case they are matched by the 1st.

It's quite possible that such behaviour is the usual way the database runs queries. You just haven't happened to see it before. It's quite possible that Oracle behaves that way as well.

The inception_date27 field is in the [H5_PREMIUM_DETAIL] (dtl) table, right?

Can you write the query as below and check it same error is raised? It may still do as a subquery does not force order of execution either.

select  
   1 as one
   ,DATEPART(yyyy,cast(inception_date27 as datetime)) as inception_yr
 from 
   ( select
        cast(arch_master_policy_no41 as numeric) as arch_master_policy_no41
        ,inception_date27
      from [DBO].[H5_PREMIUM_DETAIL]
       where len(inception_date27) != 8
   ) dtl,
     [DBO].[H5_POLICY_MASTER] polmst 
  where polmst.policy_no2 = dtl.arch_master_policy_no41      


Use the ISDATE function in your WHERE clause to make sure you have a valid date string.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜