Are arithmetic equations implicitly evaluated in NULLIF() - sql

My SQL Fundamentals 1 Exam Guide states,
select NULLIF(1234, 123+1) from dual;
The arithmetic equation is not implicitly evaluated...
However, when I submit the query below the result is null - it seems that 123+1 is evaluated (although I know that the queries are different).
select NULLIF(124, 123+1) from dual;
So, which is correct (for the 1Z0-051 Exam, anyway)? Are expressions evaluated or not?

Nullif() function evaluates both arguments and returns NULL if argument1 equal to argument2. In your case 123+1 is not equal to 1234. 123+1 is equal to 124 not 1234.
the result is null
It cannot be null in your case.
SQL> select nullif(1234, 123+1) as res
2 from dual;
RES
----------
1234
SQL> select nullif(1234, 1233+1) as res
2 from dual
3 ;
RES
----------
NULL
FOLLOW UP: My 2 cents
The above NULLIF is equivalent to the below case
CASE WHEN 1234=123+1 THEN NULL ELSE 1234 END
You cannot specify the literal NULL for the first expression.
Also from documentation
If both arguments are numeric datatypes, then Oracle Database
determines the argument with the higher numeric precedence, implicitly
converts the other argument to that datatype, and returns that
datatype. If the arguments are not numeric, then they must be of the
same datatype, or Oracle returns an error.

Related

Division by NULL in IBM DB2

In Oracle, any number divided by NULL returns NULL. I was wondering what is the case for DB2 Databases?
The whole point of it is to check whether the following expression behaves in the same way for Oracle and DB2:
SELECT a / NULLIF(b, 0) FROM some_table;
Say b=0, we would get a division by null.
The NULLIF function returns the null value if the two arguments are equal; otherwise, it returns the value of the first argument.
-NULLIF(expression,expression)-------------------------------
The result of using NULLIF(e1,e2) is the same as using the CASE expression:
CASE WHEN e1=e2 THEN NULL ELSE e1 END
Copy
When e1=e2 evaluates to unknown because one or both arguments is null, CASE expressions consider the evaluation not true. In this case, NULLIF returns the value
of the first argument.
IBM DB2 docs
So for DB2 and oracle it works same way
Db2 returns null when one of the operators in a math expression is null. For example
values (3 / cast(null as integer)) will return null.
On the other hand, the definition of NULLID is equal to that in Oracle. If both arguments are equal, it will return null.

How to SUBSTR a calculated row in SQL Query?

Original Query:
SELECT F4105.COUNCS/10000 FROM F4105
Output:
Numeric Expression
--------------------
111.1643000000000000
111.1633000000000000
111.1633000000000000
101.7654000000000000
101.7654000000000000
112.7258000000000000
I need to remove at least the last 5 zeroes. I tried to do a substring but it didn't work.
Here is the query(s) i tried:
(1)
SELECT SUBSTR((F4105.COUNCS/10000 AS 'co'),length((co)-5) FROM F4105
(2)
SELECT SUBSTR((F4105.COUNCS/10000),length((F4105.COUNCS/10000)-5)) FROM F4105
The 1st query gave me and error:
Token F4105 was not valid. Valid tokens: (.
The 2nd query worked by wrong output.
SUBSTR
00
000000
000000
000000
000000
000000
You are mixing the column alias definition in the expression. So, the correct expression is more like:
SELECT SUBSTR(F4105.COUNCS/10000, length(F4105.COUNCS/10000.0) - 5) as coFROM F4105
I wouldn't recommend doing this, however. You have a numeric expression. Just convert it to a decimal representation that you want, say:
SELECT CAST(F4105.COUNCS/10000.0 as DECIMAL(10, 5))
The syntax for SUBSTR scalar is effectively SUBSTR(expression, start-pos, for-length) IBM i 7.1->Database->Reference->SQL reference->Built-in functions->Scalar functions->SUBSTR
The LENGTH() expression shown used in the OP is specified for the second argument; i.e. the start-pos argument. As a starting position, the result of that string-length minus five calculation is conspicuously incorrect for obtaining the leftmost data; i.e. the starting-position is five bytes less than the length of the string. That would locate, of course, some insignificant zeroes five bytes from the end of the string-representation of the decimal-result of the division.
As effective correction therefore, would be either of • insert the constant integer value of 1 for the start-pos argument [thus making the LENGTH() expression become the third argument] • replace the SUBSTR scalar with the LEFT scalar.Either of those revisions would achieve something that at least resembles what is alluded as the desired output. However without either of the DDL and what should be the explicit output being expressed in the OP, the actual effect of those revised expressions could only be guessed.Anyhow, even with either of those changes, those suggested alternative character-string expressions remain as similarly poor [approaching daft] choice of expressions as the one in the OP, per lack of explicit casting; i.e. the two revised expressions suggested as possibly corrective [yet that remain similarly unlikely to yield desirable results] are:
SUBSTR((F4105.COUNCS/10000), 1,length((F4105.COUNCS/10000)-5))
LEFT((F4105.COUNCS/10000),length((F4105.COUNCS/10000)-5))
Having established data-type\length attributes using explicit casting [i.e. established even without some actual DDL to do so] in a derived-table expression that generates the input values that would produce the output shown in the OP, from a list of literal numeric values, the character-string expression in the following query ensures that only the eleven digits of decimal-precision to the right of the decimal point [i.e. the scale] are maintained; thus visually, the effect is that the trailing five digits are truncated:
with F4105 (COUNCS) as ( values
( dec( 1111643. , 9, 2 ) )
,( dec( 1111633. , 9, 2 ) )
,( dec( 1111633. , 9, 2 ) )
,( dec( 1017654. , 9, 2 ) )
,( dec( 1017654. , 9, 2 ) )
,( dec( 1127258. , 9, 2 ) )
)
SELECT cast( dec( (F4105.COUNCS/10000), 17, 11 ) as varchar(19) )
FROM F4105

ORACLE - Select Count on a Subquery

I've got an Oracle table that holds a set of ranges (RangeA and RangeB). These columns are varchar as they can hold both numeric and alphanumeric values, like the following example:
ID|RangeA|RangeB
1 | 10 | 20
2 | 21 | 30
3 | AB50 | AB70
4 | AB80 | AB90
I need to to do a query that returns only the records that have numeric values, and perform a Count on that query. So far I've tried doing this with two different queries without any luck:
Query 1:
SELECT COUNT(*) FROM (
SELECT RangeA, RangeB FROM table R
WHERE upper(R.RangeA) = lower(R.RangeA)
) A
WHERE TO_NUMBER(A.RangeA) <= 10
Query 2:
WITH A(RangeA,RangeB) AS(
SELECT RangeA, RangeB FROM table
WHERE upper(RangeA) = lower(RangeA)
)
SELECT COUNT(*) FROM A WHERE TO_NUMBER(A.RangeA) <= 10
The subquery is working fine as I'm getting the two records that have only numeric values, but the COUNT part of the query is failing. I should be getting only 1 on the count, but instead I'm getting the following error:
ORA-01722: invalid number
01722. 00000 - "invalid number"
What am I doing wrong? Any help is much appreciated.
You can test each column with a regular expression to determine if it is a valid number:
SELECT COUNT(1)
FROM table_of_ranges
WHERE CASE WHEN REGEXP_LIKE( RangeA, '^-?\d+(\.\d*)?$' )
THEN TO_NUMBER( RangeA )
ELSE NULL END
< 10
AND REGEXP_LIKE( RangeB, '^-?\d+(\.\d*)?$' );
Another alternative is to use a user-defined function:
CREATE OR REPLACE FUNCTION test_Number (
str VARCHAR2
) RETURN NUMBER DETERMINISTIC
AS
invalid_number EXCEPTION;
PRAGMA EXCEPTION_INIT(invalid_number, -6502);
BEGIN
RETURN TO_NUMBER( str );
EXCEPTION
WHEN invalid_number THEN
RETURN NULL;
END test_Number;
/
Then you can do:
SELECT COUNT(*)
FROM table_of_ranges
WHERE test_number( RangeA ) <= 10
AND test_number( RangeB ) IS NOT NULL;
Try this query:
SELECT COUNT(*)
FROM table R
WHERE translate(R.RangeA, 'x0123456789', 'x') = 'x' and
translate(R.RangeB, 'x0123456789', 'x') = 'x'
First, you don't need the subquery for this purpose. Second, using to_number() or upper()/lower() are prone to other problems. The function translate() replaces each character in the second argument with values from the third argument. In this case, it removes numbers. If nothing is left over, then the original value was an integer.
You can do more sophisticated checks for negative values and floating point numbers, but the example in the question seemed to be about positive integer values.
Coming to this question almost four years later (obviously, pointed here from a much newer thread). The other answers show how to achieve the desired output, but do not answer the OP's question, which was "what am I doing wrong?"
You are not doing anything wrong. Oracle is doing something wrong. It is "pushing" the predicate (the WHERE condition) from the outer query into the inner query. Pushing predicates is one of the most basic ways in which the Optimizer makes queries more efficient, but in some cases (and the question you ask is a PERFECT illustration) the result is not, in fact, logically equivalent to the original query.
There are ways to prevent the Optimizer from pushing predicates; or you can write the query in a better way (as shown in the other answers). But if you wanted to know why you saw what you saw, this is why.

Coalesce and Case-When with To_Date not working as expected (Postgres bug?)

I'm using Postgres 9.1. The following query does not work as expected. Coalesce should return the first non-null value. However, this query returns null (1?) instead of the date (2).
select COALESCE(
TO_DATE('','yyyymmdd'), --(1)
TO_DATE('20130201','yyyymmdd') --(2)
);
--(1) this evaluates independently to null
--(2) this evaluates independently to the date,
-- and therefore is the first non-null value
What am I doing wrong? Any workaround?
Edit: This may have nothing to do with Coalesce at all. I tried some experiments with Case When constructs; it turns out, Postgres has this big ugly bug where it treats TO_DATE('','yyyymmdd') as not null, even though selecting it returns null.
[PS: Strike-out above to avoid misleading. Postgres doesn't have a bug, but rather does not treat empty strings as null. See answer.]
SELECT TO_DATE('','yyyymmdd');
doesn't evaluates to NULL since you passing an empty string instead of NULL as an argument to TO_DATE()
This will successfully evaluate to NULL
SELECT TO_DATE(NULL,'yyyymmdd');
If you expect an empty string and want to treat it as a NULL you can use NULLIF()
SELECT TO_DATE(NULLIF(dt, ''),'yyyymmdd')
FROM
(
SELECT CAST('' AS VARCHAR(32)) dt
) q
That being said your sample code that evaluates (1) as NULL
SELECT COALESCE(
TO_DATE(NULLIF('', ''),'yyyymmdd'), --(1)
TO_DATE(NULLIF('20130201',''),'yyyymmdd') --(2)
);
and returns
| COALESCE |
-----------------------------------
| February, 01 2013 00:00:00+0000 |
Here is SQLFiddle demo

Sql Server: CASE Statement does unexpected behavior when comparing to NULL

Given:
The following Select statement:
select case NULL
when NULL then 0
else 1
end
Problem:
I'm expecting this to return 0 but instead it returns 1. What gives?
Generally speaking, NULL is not something you should attempt to compare for equality, which is what a case statement does. You can use "Is NULL" to test for it. There is no expectation that NULL != NULL or that NULL = NULL. It's an indeterminate, undefined value, not a hard constant.
-- To encompass questions in the comments --
If you need to retrieve a value when you may encounter a NULL column, try this instead:
Case
When SomeColumn IS NULL
Then 0
Else 1
End
I believe that should work. As far as your original post is concerned:
Select Case NULL
When NULL then 0 // Checks for NULL = NULL
else 1 // NULL = NULL is not true (technically, undefined), else happens
end
The trouble is that your Case select automatically attempts to use equality operations. That simply doesn't work with NULL.
I was going to add this as a comment to Aaron's answer, but it was getting too long, so I'll add it as another (part of the) answer.
The CASE statement actually has two distinct modes, simple and searched.
From BOL:
The CASE expression has two formats:
The simple CASE expression compares an expression to a set of simple expressions to determine the result.
The searched CASE expression evaluates a set of Boolean expressions to determine the result.
When the simple CASE (your example) does what it describes as comparison it does an equality comparison - i.e. =
This is clarified in the later documentation:
The simple CASE expression operates by comparing the first expression
to the expression in each WHEN clause for equivalency. If these
expressions are equivalent, the expression in the THEN clause will be
returned.
Allows only an equality check.
Because anything = NULL is always false in ANSI SQL (and if you didn't know this, you need to read up on NULLs in SQL more generally, particularly also with the behavior in the other searched comparison - WHERE x IN (a, b, c)), you cannot use NULL in a simple case and have it ever be compared to a value, with a NULL either in the initial expression or in the list of expressions to be compared against.
If you want to check for NULL, you will have to use an IF/ELSE construct or the searched CASE with a full expression.
I agree that it's kind of unfortunate there is no version which supports an IS comparison to make it easier to write:
select case colname
when IS NULL then 0
else 1
end
Which would make writing certain long CASE statements easier:
select case colname
when IS NULL then ''
when 1 then 'a'
when 2 then 'b'
when 3 then 'c'
when 4 then 'd'
else 'z'
end
But that's just wishful thinking...
An option is to use ISNULL or COALESCE:
select case COALESCE(colname, 999999) -- 999999 is some value never used
when 999999 then ''
when 1 then 'a'
when 2 then 'b'
when 3 then 'c'
when 4 then 'd'
else 'z'
end
But it isn't always a great option.
In addition to the other answers, you need to change the syntax for CASE slightly to do this:
SELECT CASE
WHEN NULL IS NULL THEN 0
ELSE 1
END;
Using the value in your syntax implicitly uses an equals comparison. NULL is unknown, and so is NULL = NULL, so with your current code you will always get zero 1 (geez I did it too).
To get the behavior you want, you can use SET ANSI_NULLS ON; however note that this can change other code in ways you may not be able to predict, and the setting is deprecated - so it will stop working at all in a future version of SQL Server (see this SQL Server 2008 doc).
You need to use the IS NULL operator. Standard comparison operators do not work with NULL.
Check out these MSDN articles about Null that may be useful:
IS [NOT] NULL (Transact-SQL)
Null Values