Reputation: 401
Preparation
create table t (x varchar(18))
insert into t (x) values ('8003372602728'), ('a')
Query #1:
select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100
Result for query #1:
y
--------------------
8003372602728
(1 row affected)
Query #2:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
where y is not null
Result for query #2:
y
--------------------
8003372602728
Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar to bigint.
What is the reason of the error in the result for query #2?
Upvotes: 1
Views: 61
Reputation: 7918
First, note the difference between these queries:
select cast(x as bigint) y
from t
where isnumeric(x) = 1
and cast(x as bigint) > 100;
select cast(x as bigint) y
from t
where cast(x as bigint) > 100
and isnumeric(x) = 1;
The first one works and the second fails with the conversion error. The first one works because isnumeric is evaluated first and only numeric values are evaluated. With the second the query is evaluating: CAST(x as bigint)
first. In theory, neither query should work because filters are not guaranteed to be evaluated in any specific order.
Next, this works:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
----WHERE a.y > 100
... But, once you uncomment the WHERE clause it fails. This is because the WHERE clause on the Outer query is evaluating x before determining if ISNUMERIC is true.
This works:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
WHERE isnumeric(a.y) = 1
But, again, when I change the outer query to include a filter for y > 100...
select *
from
( select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
WHERE isnumeric(a.y) = 1 and CAST(a.y AS bigint) > 100
It fails. Why? Because order of evaluation is not guaranteed.
TRY_CAST solves two problems:
First, isnumeric is a proprietary legacy function which does not do what most people think. isnumeric thinks all this is numeric:
SELECT ISNUMERIC('.'), ISNUMERIC('$'), ISNUMERIC('-'), ISNUMERIC('+'), ISNUMERIC('$+.');
Second, it solves the order of evaluation issue. This query works:
select *
from
(select TRY_CAST(x as bigint) y
from t
where TRY_CAST(x AS bigint) IS NOT NULL ) a
WHERE y > 100; -- Implies that it's NOT NULL
Upvotes: 2