I'm having trouble understanding why the following queries return the given results.
Query 1:
SELECT TO_NUMBER('$1,000.95', '$999,999.999')
FROM dual;
returns 1000.95.
Why doesn't this query return $1000.95 or $1,000.95? Does the TO_NUMBER function remove all formatting (other than .)?
Query 2:
SELECT TO_NUMBER('1,000.95', '$999,999.999')
FROM dual;
returns ORA-01722: invalid number.
I suppose I understand this one a little better - the format mask doesn't match the number format. Is there a way to return a $ in the number (other than using CONCAT or ||)?
The function TO_NUMBER returns a number, the $ character is ASCII and is never going to be represented in a number field. To add a $ character you will need to represent the number as a string.
SELECT TO_NUMBER('$1,000.95', 'L999G999D999') FROM dual;
Related
This query:
select nvl(0.75,0) from dual
gives me 0.75 (numeric) but this query:
select decode(1,0,null,0.75) from dual
gives me '.75' (string).
Why?
I tried to fix this by changing the second query to:
select decode(1,0,null,to_char(0.75,'0.99')) from dual
but in my actual code the 0.75 will be a field (NUMBER) that may have a different number of decimal places and I'm not suppose to add/remove anything from that value.
Any ideas on how to fix the missing zero issue but still support all possible decimal lengths?
It's because the 3rd parameter of your decode statement is NULL; as per the documentation1 (my emphasis).
Oracle automatically converts expr and each search value to the data type of the first search value before comparing.... If the first result has the data type CHAR or if the first result is null, then Oracle converts the return value to the data type VARCHAR2.
In your case the first result is NULL, which Oracle treats as a VARCHAR2. Your return value is being implicitly converted to a VARCHAR2. If you changed your DECODE() to the following you'd get a number:
select decode(1, 0, 0, 0.75)
and you could achieve your NULL by using the NULLIF() function:
select nullif(decode(1, 0, 0, 0.75), 0) ...
It's better to use a CASE statement, which enforces that all returned datatypes are the same:
select case 1 when 0 then null
else 0.75
end ...
1. which I've been caught out on as well.
You can use
select decode(1,0,to_number(null),0.75) from dual
In the first case, nvl() is returning a numeric value. How to display that is up to the program you are using to run your queries. TOAD displays it like you said, 0.75.
In the second example, decode() is returning a varchar2. When Oracle converts a number to a string without any formatting, this is what you get, i.e. ".75".
From the Oracle docs on decode():
If the first result has the datatype CHAR or if the first result is
null, then Oracle converts the return value to the datatype VARCHAR2.
You could use a number format and rtrim() to achieve your purpose, e.g.:
select rtrim(to_char(.75, '9990.99999999999999'),'0') from dual;
Result:
0.75
select to_number(decode(1,0,null,0.75)) from dual
I don't understand why these two code results are identically the same? I thought if I have quotes it just concatenate strings. Why not first one is 300.5100? I know second one is 400.5 anyway. Thank you
Select to_char('300.5' + '100') From Dual;
Select to_char(300.5 + 100) From Dual;
To concatenate strings in SQL you have to use ||. The + is only there to add numbers. If you didn't pass '300.5' + '100' you would simply get an error, e.g. the following is invalid SQL:
select '300.5' + '100'
from dual;
But as to_char() expects a number as the input parameter Oracle implicitly converts those strings to numbers and then adds them, just like in the second statement.
concatenation is
'xxx' || 'yyy'
your example allows the literals to be converted to numeric then treated as normal numbers.
SELECT inv_no,NVL2(inv_amt,inv_date,'Not Available')
FROM invoice;
SELECT inv_no,NVL2(inv_amt,inv_amt*.25,'Not Available') FROM invoice;
getting
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-01722: invalid number
respectively
Please suggest what are the datatypes I can give in expr1 and expr2.
Also Please tell me how this is right?
SELECT inv_no,NVL2(inv_date,sysdate-inv_date,sysdate)
FROM invoice
The 2nd and 3rd parameters to NVL2 should be the same datatype. So, assuming inv_date is a DATE, you'd need to have that as a varchar2 like;
SELECT inv_no, NVL2(inv_amt, to_char(inv_date, 'dd-mon-yyyy hh24:mi:ss'), 'Not Available') FROM ...
or whatever format string you wanted. Otherwise Oracle will convert the 3rd parameter 'Not Available' to match the 2nd parameter's data type. This will try to convert 'Not Available' to a date and crash.
Similarly in the 2nd example, you have to convert the inv_amt*.25 to a char viato_char, e.g. to_char(inv_amt*.25).
Your first 2 examples attempt to have a date / numeric and text results for the same field. This will cause an error when Oracle attempts to convert this text to either types. You'll need to use the to_char(field) function on the date / numeric fields to convert them to text.
Lastly a date is in fact a number added to a databases base date. For example a date is a number of base day and the decimal it has is the ratio of a day, e.g. 0.5 is 12 hours, and the database has a base date, e.g. 01-Jan-1900 or 01-Jan-2000. This is why when you do date - date the result is a number and a date can also be represented as a number.
Refer to: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions106.htm
The 2nd argument should be either character or numeric. The 2nd argument gives datatype to which the 3rd argument will be implictly converted. So:
1. The first one SELECT inv_no,NVL2(inv_amt,inv_date,'Not Available') FROM invoice; is incorrect as you cannot have DATE as a 2nd argument.
The same way:
SELECT NVL2(NULLIF(1, 1), SYSDATE, 'A') FROM DUAL; -- FAILS;
SELECT NVL2(NULLIF(1, 0), SYSDATE, 'A') FROM DUAL; -- FAILS;
SELECT NVL2(NULLIF(1, 1), 'A', SYSDATE) FROM DUAL; -- PASS; returns SYSDATE (as char datatype)
SELECT NVL2(NULLIF(1, 0), 'A', SYSDATE) FROM DUAL; -- PASS; returns 'A';
2. The second is invalid too: SELECT inv_no,NVL2(inv_amt,inv_amt*.25,'Not Available') FROM invoice; Oracle tries to implicitly convert 3rd argument to the datatype of the 2nd arugment, meaning it tries to convert 'Not available' to numeric value.
It will work when you swap 2nd and 3rd argument:
SELECT inv_no,NVL2(inv_amt,'Not Available',inv_amt*.25) FROM invoice;
inv_amt*.25 will be implicitly converted to character data; This example is useless as it will get you NULL*.25 making it NULL.
What you can do however is you can make both parameters as characters by converting numeric result to character:
SELECT inv_no,NVL2(inv_amt,TO_CHAR(inv_amt*.25), 'Not Available') FROM invoice;
It will make some sense particularly when you want to display currency.
3. The third one is more complex
The only way I can explain is that the 2nd argument gets converted to character before dealing with the third one. That way the third one will get converted to characters too. This would make sense given the two cases:
SELECT NVL2(2, SYSDATE - TO_DATE('10-JAN-83'), SYSDATE) from dual; -- PASSES
SELECT NVL2(2, 2.2, SYSDATE) from dual; -- FAILS.
Finally: I would advise to stick to Oracle's documentation and use explicit conversion.
I am trying to do a comparison based on a column. Let's say, if column>5.
select * where column>5
The column contains non digits. I thought Oracle allows one to compare strings (like Java).
Apparently this is not allowed.
ORA-01722: invalid number
01722. 00000 - "invalid number"
Is there a way to do comparisons with non numeric fields?
Thanks
Yes, you have to put the 5 in quotes :
select * from table where column > '5'
To shed a bit more light on why it doesn't work.
When using a number literal 5 instead of a character literal '5' Oracle does an implicit data type conversion and tries to convert all values in your table to a number. That's why you get that error.
You should never rely on implicit data type conversion. That is bound to give you trouble sometime.
Now if you correctly compare a character literal ('5') against a character column, no data type conversion is needed and no error occurs.
However: if you expect Oracle to actually do a numeric comparison then you are mistaken. Character string are based on ASCII values. Therefor the (character) value '10' is lower than the (character) value '2' because the first character '1' is ranked lower than '2'.
If the column is varchar2, then this:
select * from some_table where some_column > 5
... does an implicit conversion of all the column values to numbers, so you're really doing:
select * from some_table where to_number(some_column) > 5
It's the to_number() that's causing the ORA-01722, even though you can't see it, when it hits a value that is not numeric. The function being called on the column value also stops any index being used (oversimplifying a bit).
You can stop it failing, and let it use the index if there is one, by doing where some_column > '5' as other have said, or where some_column > to_char(5). But you need to be careful doing the comparison as it will still be a string comparison, not a numeric one; so '10' will not be seen as > '5'; and your NLS sorting parameters might produce results you aren't expecting. Or more importantly, someone else's NLS parameters - when you put this live for example - might product results you aren't expecting and which don't match the ones you got in your environment. See the documentation for more.
You should use number columns to hold numeric values, date columns to hold dates, etc., and varchar2 only to hold text values.
you can use to_char function
select * from table where column > to_char(5)
You are missing the table:
select * where FROM tablename column>5
But this only works if column is a number. If not, you can't use >.
To compare strings, you can use LIKE or STRCMP(), check examples of them HERE.
As stated by #Gerrat, you can also use > and < but the types of both sides must be compatible (number with number or text with text). To find more about it, check THIS.
Be aware that in text comparison it will compare each character individually so '11' will be < that '2'.
i could not understand why following code
SQL>
Select to_number('1234.64', '9999.9') from Dual;
returns this number 1234.6?is it something like rounding ,truncation or?please help me to understand this code,i know to_number functions,i have used many times this code for simple chars,but here it is not clear anything
This looks a lot like Oracle, but I suspect that the result would be similar in any SQL that used to_number.
The to_number function takes two arguments: the string to be converted to a number, and the format string for the conversion.
In the example, '12345.64' is the string to be converted, while '9999.9' is the format string. In this format string, a 9 stands for a digit while a . stands for the decimal point.
So the function is asking to convert the string '12345.64' to a number with up to 4 digits to the right of the decimal point, and only 1 digit after the decimal point.
The second argument is optional - under normal circumstances, I would omit it.
You should use
SELECT to_number('1234.64', '9999.99') from Dual;
Your mask tells engine you want just one decimal, so number gets rounded.
If you want to get exact number, don't specify any mask:
SELECT to_number('1234.64') from Dual;