Specify order of (T)SQL execution - sql

I have seen similar questions asked elsewhere on this site, but more in the context of optimization.
I am having an issue with the order of execution of the conditions in a WHERE clause. I have a field which stores codes, most of which are numeric but some of which contain non-numeric characters. I need to do some operations on the numeric codes which will cause errors if attempted on non-numeric strings. I am trying to do something like
WHERE isnumeric(code) = 1
AND CAST(code AS integer) % 2 = 1
Is there any way to make sure that the isnumeric() executes first? If it doesn't, I get an error...
Thanks in advance!

The only place order of evaluation is guaranteed is CASE
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS integer) % 2
END = 1
Also just because it passes the isnumeric test doesn't guarantee that it will successfully cast to an integer.
SELECT ISNUMERIC('$') /*Returns 1*/
SELECT CAST('$' AS INTEGER) /*Fails*/
Depending upon your needs you may find these alternatives preferable.

Why not simply do it using LIKE?:
Where Code Not Like '%[^0-9]%'
Btw, either using my solution or using IsNumeric, there are some edge cases which might lead one to using a UDF such as 1,234,567 where IsNumeric will return 1 but Cast will throw an exception.

Why not use a CASE statement to say something like:
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS int) % 2 = 1
ELSE /* What ever else if not numeric */ END

You could do it in a case statement in the select clause, then limit by the value in an outer select
select * from (
select
case when isNum = 1 then CAST(code AS integer) % 2 else 0 end as castVal
from (
select
Case when isnumeric(code) = 1 then 1 else 0 end as isNum
from table) t
) t2
where castval = 1

Related

CASE expression - add one more condition to the WHEN part

I have created a PL/SQL function where I have a case expression in a SQL query. This is working fine, but when I add another when condition it will not compile. Even if I use when ... and 2 > 1, this is also not compiling.
In the below code, the commented part is not working properly.
What I want is to add one more check in my when clause. Please advise.
create or replace function FUNCTION_NAME (date1 in varchar2,value1 in varchar2)
RETURN date
IS
date2 date;
BEGIN
SELECT D DATE2
INTO DATE2 FROM (SELECT CASE (SELECT TO_DATE(MAX(G.DATE3),'DD-MON-YYYY')
FROM TABLE1 G,
TABLE2 N
WHERE G.DATE3=N.DATE3)
WHEN LAST_DAY(TO_DATE(DATE1,'DD-MON-YYYY'))
/* AND MONTHS_BETWEEN (LAST_DAY(TO_DATE(SYSDATE)),
LAST_DAY(TO_DATE(TO_CHAR(DATE1),'DD-MON-YYYY'))) */
THEN LAST_DAY(TO_DATE(DATE1,'DD-MON-YYYY'))
ELSE
TO_DATE('31-DEC-99','DD-MON-YYYY')
END D
FROM DUAL);
RETURN DATE2;
END;
What you have is a case expression (not a case statement).
Case expressions are of two kinds: "simple" (case <expr> when val1 then ... when val2 then... etc.) and "searched" ( case when condition1 then ... when condition2 then ... etc.)
You wrote your case expression as a simple case expression. You can't, then, add conditions to the WHEN part. You must change the case expression to be "searched" all the way through.
case when (select ...) = last_day(...) AND <your commented condition> THEN .....
EDIT - copying part of a clarifying comment below my Answer.
Simple case expression:
case x when 1 then ....
Can also be written as searched case expression:
case when x = 1 then ....
These two are logically equivalent. However, if we want to add "AND 3 > 1" to the WHEN part, that works only in the searched form of the case expression.
There are two flavours of CASE.
Simple CASE:
select case dummy
when 'X' then 1
end as case_demo
from dual;
Searched CASE:
select case
when dummy = 'X' then 1
end as case_demo
from dual;
In your query you are mixing them like this, which won't work:
select case dummy
when 'X' and 1 = 1
then 1
end as case_demo
from dual;
If you switch to a "searched CASE", then you can add more when conditions:
select case
when dummy = 'X' and 1 = 1
then 1
end as case_demo
from dual;

checking for invalid values sql

I have a stored procedure where I have a condition to check whether a Rating Code is 1,2 or 3 in the where clause. Something like this:
WHERE
CONVERT(INT, LEFT(RatingCode, 1)) IN (1,2,3) AND
At times when there are bad values in RatingCode column the above line throws an error. Hence I came up with the below solution:
WHERE
CASE WHEN ISNUMERIC(LEFT(RatingCode, 1)) = 1
THEN CASE WHEN CONVERT(INT, LEFT(RatingCode, 1)) IN (1,2,3)
THEN 1
ELSE 0
END
ELSE 0
END = 1 AND
Here if there is an invalid value(non numeric) in RatingCode column then I want to ignore that record. Is my above solution a good one? Or is there any better solution?
In that specific case, you could also just use
WHERE
LEFT(RatingCode, 1) IN ('1','2','3') AND
Besides that, also string comparisons are allowed in tsql.
WHERE
LEFT(RatingCode, 1) BETWEEN '1' AND '3' AND
This does not throw an error for non-numeric letters.

SQL query: convert

I'm trying to read a column from a database using a SQL query. The column consists of empty string or numbers as strings, such as
"7500" "4460" "" "2900" "2640" "1850" "" "2570" "9050" "8000" "9600"
I'm trying to find the right sql query to extract all the numbers (as integers) and removing the empty ones, but I'm stuck. So far I've got
SELECT *
FROM base
WHERE CONVERT(INT, code) IS NOT NULL
Done in program R (package sqldf)
If all columns are valid integers, you could use:
select * , cast(code as int) IntCode
from base
where code <> ''
To prevent cases when field code is not a valid number, use:
select *, cast(codeN as int) IntCode
from base
cross apply (select case when code <> '' and not code like '%[^0-9]%' then code else NULL end) N(codeN)
where codeN is not null
SQL Fiddle
UPDATE
To find rows where code is not a valid number, use
select * from base where code like '%[^0-9]%'
select *
from base
where col like '[1-9]%'
Example: http://sqlfiddle.com/#!6/f7626/2/0
If you don't need to test for the number being valid, ie. a string such as '909XY2' then this may run marginally faster, more or less depending on the size of the table
Is this what you want?
SELECT (case when code not like '%[^0-9]%' then cast(code as int) end)
FROM base
WHERE code <> '' and code not like '%[^0-9]%';
The conditions are repeated in the where and case on purpose. SQL Server does not guarantee that where filters are applied before logic in the select, so you can get an error with conversions. More recent versions of SQL Server have try_convert() to fix this problem.
Using sqldf with the default sqlite database and this test data:
DF <- data.frame(a = c("7500", "4460", "", "2900", "2640", "1850", "", "2570",
"9050", "8000", "9600"), stringsAsFactors = FALSE)
try this:
library(sqldf)
sqldf("select cast(a as aint) as aint from DF where length(a) > 0")
giving:
aint
1 7500
2 4460
3 2900
4 2640
5 1850
6 2570
7 9050
8 8000
9 9600
Note In plain R one could write:
transform(subset(DF, nchar(a) > 0), a = as.integer(a))

Execute a WHERE clause before another one

I have the following statement
SELECT * FROM foo
WHERE LEN(bar) = 4 AND CONVERT(Int,bar) >= 5000
The values in bar with a length of exactly 4 characters are integers. The other values are not integers and therefore it throws an conversion exception, when trying to convert one of them to an integer.
I thought it's enough to put the LEN(bar) before the CONVERT(Int,bar) >= 5000. But it's not.
How can I kind of prioritize a specific where clause? In my example I obviously want to select all values with a length of 4, before converting and comparing them.
6 answers and 5 of them don't work (for SQL Server)...
SELECT *
FROM foo
WHERE CASE WHEN LEN(bar) = 4 THEN
CASE WHEN CONVERT(Int,bar) >= 5000 THEN 1 ELSE 0 END
END = 1;
The WHERE/INNER JOIN conditions can be executed in any order that the query optimizer determines is best. There is no short-circuit boolean evaluation.
Specifically for your question, since you KNOW that the data with 4-characters is a number, then you can do a direct lexicographical (text) comparison (yes it works):
SELECT *
FROM foo
WHERE LEN(bar) = 4 AND bar > '5000';
try this
SELECT bar FROM
(
SELECT CASE
WHEN LEN(bar) = 4 THEN CAST( bar as int)
ELSE CAST(-1 as int) END bar
FROM Foo
) Foo
WHERE bar>5000

Oracle: How can I get a value 'TRUE' or 'FALSE' comparing two NUMBERS in a query?

I want to compare two numbers. Let's take i.e. 1 and 2.
I've tried to write the following query but it simply doesn't work as expected (Toad says: ORA-00923: FROM keyword not found where expected):
SELECT 1 > 2 from dual
The DECODE is something like a Switch case, so how can I get the result of an expression evalutation (i.e. a number comparison) putting it in the select list?
I have found a solution using a functions instead of an expression in the SELECT LIST: i.e.
select DECODE(SIGN(actual - target)
, -1, 'NO Bonus for you'
, 0,'Just made it'
, 1, 'Congrats, you are a winner')
from some_table
Is there a more elegant way?
Also how do I compare two dates?
There is no boolean types in sql (at least in oracle).
you can use case:
SELECT CASE when 1 > 2 THEN 1 ELSE 0 END FROM dual
But your solution (decode) is also good, read here
The SIGN() function is indeed probably the best way of classifying (in)equality that may be of interest to you if you want to test a > b, a = b and a < b, and it will accept date-date or numeric-numeric as an argument.
I'd use a Case statement by preference, rather than a decode.
Select
case sign(actual-target)
when -1 then ...
when 0 then ...
when 1 then ...
end
SELECT (CASE
WHEN (SIGN(actual - target) > 0 ) THEN
'NO Bonus for you'
ELSE
'Just made it' END)
FROM dual
you can compare two dates with sql
METHOD (1):
SELECT TO_DATE('01/01/2012') - TO_DATE('01/01/2012')
FROM DUAL--gives zero
METHOD (2):
SELECT CASE
when MONTHS_BETWEEN('01/01/2012','01/01/2010') > 0
THEN 'FIRST IS GREATER'
ELSE 'SECOND IS GREATER OR EQUAL' END
FROM dual
sorry i cant format the code the formatting toolbar disappeared !
do any one know why?