Is "NUMBER" and "NUMBER(*,0)" the same in Oracle? - sql

In Oracle documentation it is mentioned that
NUMBER (precision, scale)
If a precision is not specified, the column stores values as given. If
no scale is specified, the scale is zero.
But NUMBER (without precision and scale) is also accepting floating point numbers (34.30) but according to documentation if scale is not specified it should be zero scale by default so it should allow only integers, am I wrong?.
And in another questions it is mentioned that
default precision is 38, default scale is zero
So NUMBER and NUMBER(*,0) should be equal but they are not.
Where am I wrong?

I think the sentence in the documentation
If a precision is not specified, the column stores values as given. If no scale is specified, the scale is zero.
is a bit confusing. The scale is zero if a precision is specified and a scale is not specified. So, for example, NUMBER(19) is equivalent to NUMBER(19,0). NUMBER, by itself, will have 38 digits of precision but no defined scale. So a column defined as a NUMBER can accept values of any scale, as long as their precision is 38 digits or less (basically, 38 numerical digits with a decimal point in any place).
You can also specify a scale without a precision: NUMBER(*, <scale>), but that just creates the column with 38 digits of precision so I'm not sure it's particularly useful.
The table How Scale Factors Affect Numeric Data Storage on this page might be helpful.

The default of scale is not zero, which has no value in it. Hence it can accept any value between -84 to 127. If you limit it to zero then it will not accept any presicion even the value contains the scale value
create table aaaaa
(
sno number(*,0),
sno1 number
);
The user_tab_columns will give you the value of your precision and scale
SQL> select column_name,data_precision,data_scale from user_tab_columns where ta
ble_name = 'AAAAA';
COLUMN_NAME DATA_PRECISION DATA_SCALE
------------------------------ -------------- ----------
SNO 0
SNO1
SQL>
Please find the below workings
SQL> select * from aaaaa;
no rows selected
SQL> insert into aaaaa values (123.123123,123123.21344);
1 row created.
SQL> commit;
Commit complete.
SQL> select * from aaaaa;
SNO SNO1
---------- ----------
123 123123.213
SQL>

This parts of Oracle documentation makes it absolutely clear:
Specify an integer using the following form:
NUMBER(p)
This represents a fixed-point number with precision p and scale 0 and is equivalent to NUMBER(p,0).
and
Specify a floating-point number using the following form:
NUMBER
The absence of precision and scale designators specifies the maximum range and precision for an Oracle number.
The meaning of the star precision is documented here and means the precision of 38

Another squirrelly case but one I faced... if the table you're inserting into contains a trigger, you should probably examine if any of its procedural flow includes attempting to convert something into a NUMBER...

Related

Shouldn't binary_double store a higher value than number in Oracle?

Considering the following test code :
CREATE TABLE binary_test (bin_float BINARY_FLOAT, bin_double BINARY_DOUBLE, NUM NUMBER);
INSERT INTO binary_test VALUES (4356267548.32345E+100, 4356267548.32345E+2+300, 4356267548.32345E+100);
SELECT CASE WHEN bin_double>to_binary_double(num) THEN 'Greater'
WHEN bin_double=to_binary_double(num) THEN 'Equal'
WHEN bin_double<to_binary_double(num) THEN 'Lower'
ELSE 'Unknown' END comparison,
A.*
FROM binary_test A;
I've tried to see which one stores higher values. If I try to add E+300 for the number and binary_float columns, it returns numeric overflow error. So, I thought I could store a greater value with the binary_float.
However, when I tried to check it, it shows a lower value, and with the case comparison it says it is lower too. Could you please elaborate this situation?
You are inserting the value 4356267548.32345E+2+300 into the binary double column. That evaluates to 4356267548.32345E+2, which is 435626754832.345, plus 300 - which is 435626755132.345 (or 4.35626755132345E+011, which becomes 4.3562675513234497E+011 when converted to binary double). That is clearly lower than 4356267548.32345E+100 (or 4.35626754832345E+109, which becomes 4.3562675483234496E+109 when converted to binary double).
Not directly relevant, but you should also be aware that you're providing a decimal number literal, which will be implicitly converted to binary double during insert. So you can't use 4356267548.32345E+300, as that is too large for the number data type. If you want to specify a binary double literal then you need to append a d to it, i.e. 4356267548.32345E+300d; but that is still too large.
The highest you can go with that numeric part is 4356267548.32345E+298d, which evaluates to 4.3562675483234498E+307 - just below the data type limit of 1.79769313486231E+308; and note the loss of precision.
db<>fiddle

How do you update float data type in SQL column to hold numbers with upto 20 scale

I have a SQL table with a column having float data type with default precision.
I want this column to hold data with upto 20 digits after the decimal point.
How do I update existing column to do this?
SQL Server: The literal answer here would be "you cannot". If you look at the documentation, you'll see that the float type only supports two precisions - either 7 or 15 digits.
Answers provided so far all seem to be advocating a switch to a different datatype (decimal) but don't highlight that this comes with its own drawbacks - notably that the decimal type supports a far smaller range. float can support a range from - 1.79E+308 to -2.23E-308, 0 and 2.23E-308 to 1.79E+308 whereas decimal only supports a range of ~ -10^38 to 10^38 (and that, only if you are using a 0 scale). If using a scale of 20 then the range is limited to ~ -10^18 to 10^18.
It depends on how much value You want to store to the left of the decimal point. Lets say only number from 0-9 and 20 digits on the right.
Alter table tablename altercolumn columnname(21,20)
try it
ALTER TABLE MyTable ALTER COLUMN MyColumndecimal decimal(38,20)
decimal(precision, scale)
its mean left side = 18, right side = 20
Just put decimal(precision, scale), replacing the precision and scale with your desired values.
ALTER TABLE TableName ALTER COLUMN ColumnName decimal(24,20)

I have created CHECK constraint but receiving error message

create sequence student_studentid_seq
increment by 10
start with 100
nocycle;
create table student
(studentid number(10),
name varchar2(30) not null,
ss# number(9) unique,
gpa number(2,3) not null,
constraint student_studentid_pk PRIMARY KEY (studentid),
constraint student_gpa_ck CHECK (GPA >= 0) );
insert into student (studentid, name, ss#, gpa)
values(student_studentid_seq.NEXTVAL,'Draze Katan', 323456789,1);
receiving error message:
Error starting at line 29 in command:
insert into student (studentid, name, ss#, gpa)
values(student_studentid_seq.NEXTVAL,'Draze Katan', 323456789,1)
Error report:
SQL Error: ORA-01438: value larger than specified precision allowed for this column
01438. 00000 - "value larger than specified precision allowed for this column"
*Cause: When inserting or updating records, a numeric value was entered
that exceeded the precision defined for the column.
*Action: Enter a value that complies with the numeric column's precision,
or use the MODIFY option with the ALTER TABLE command to expand
the precision.
So it appears error message is for next constraint:constraint student_gpa_ck CHECK (GPA >= 0) );
In insert statement if I enter '0' for GPA raw will be inserted but anything more I will receive error message.
This is one of my exercise questions, I can't figure out. I just need hint where mistake is not full resolution. Please if you could help me out.
The issue is in the way you create the table, in particular in the column GPA.
You are using number(2, 3), which looks like "build a number with 2 total digits and 3 decimal digits".
In oracle documentation you find a better explanation about the NUMBER data type, its attributes and what things like number(2,3) mean:
Specify a fixed-point number using the following form:
NUMBER(p,s) where:
p is the precision, or the maximum number of significant decimal
digits, where the most significant digit is the left-most nonzero
digit, and the least significant digit is the right-most known digit.
Oracle guarantees the portability of numbers with precision of up to
20 base-100 digits, which is equivalent to 39 or 40 decimal digits
depending on the position of the decimal point.
s is the scale, or the number of digits from the decimal point to the
least significant digit. The scale can range from -84 to 127.
Positive scale is the number of significant digits to the right of the
decimal point to and including the least significant digit.
Negative scale is the number of significant digits to the left of the
decimal point, to but not including the least significant digit. For
negative scale the least significant digit is on the left side of the
decimal point, because the actual data is rounded to the specified
number of places to the left of the decimal point. For example, a
specification of (10,-2) means to round to hundreds.
Scale can be greater than precision, most commonly when e notation is
used. When scale is greater than precision, the precision specifies
the maximum number of significant digits to the right of the decimal
point. For example, a column defined as NUMBER(4,5) requires a zero
for the first digit after the decimal point and rounds all values past
the fifth digit after the decimal point.
For example:
SQL> create table tabError( a number (2, 3));
Table created.
SQL> insert into tabError values (1);
insert into tabError values (1)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into tabError values (0.1);
insert into tabError values (0.1)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into tabError values (0.01);
1 row created.
If you need 2 digits for the integer part and 3 for decimals, you need number(5, 3) or, according to Mathguy's comment, if you need numbers with one integer digit and 2 decimals, you need number(3,2).

Value of real type incorrectly compares

I have field of REAL type in db. I use PostgreSQL. And the query
SELECT * FROM my_table WHERE my_field = 0.15
does not return rows in which the value of my_field is 0.15.
But for instance the query
SELECT * FROM my_table WHERE my_field > 0.15
works properly.
How can I solve this problem and get the rows with my_field = 0.15 ?
To solve your problem use the data type numeric instead, which is not a floating point type, but an arbitrary precision type.
If you enter the numeric literal 0.15 into a numeric (same word, different meaning) column, the exact amount is stored - unlike with a real or float8 column, where the value is coerced to next possible binary approximation. This may or may not be exact, depending on the number and implementation details. The decimal number 0.15 happens to fall between possible binary representations and is stored with a tiny error.
Note that the result of a calculation can be inexact itself, so be still wary of the = operator in such cases.
It also depends how you test. When comparing, Postgres coerces diverging numeric types to a type that can best hold the result.
Consider this demo:
CREATE TABLE t(num_r real, num_n numeric);
INSERT INTO t VALUES (0.15, 0.15);
SELECT num_r, num_n
, num_r = num_n AS test1 --> FALSE
, num_r = num_n::real AS test2 --> TRUE
, num_r - num_n AS result_nonzero --> float8
, num_r - num_n::real AS result_zero --> real
FROM t;
db<>fiddle here
Old sqlfiddle
Therefore, if you have entered 0.15 as numeric literal into your column of data type real, you can find all such rows with:
SELECT * FROM my_table WHERE my_field = real '0.15'
Use numeric columns if you need to store fractional digits exactly.
Your problem originates from IEEE 754.
0.15 is not 0.15, but 0.15000000596046448 (assuming double precision), as it can not be exactly represented as a binary floating point number.
(check this calculator)
Why is this a problem? In this case, most likely because the other side of the comparison uses the exact value 0.15 - through an exact representation, like a numeric type. (Cleared up on suggestion by Eric)
So there are two ways:
use a format that actually stores the numbers in decimal format - as Erwin suggested
(or at least use the same type across the board)
use rounding as Jack suggested - which has to be used carefully (by the way this uses a numeric type too, to exactly represent 0.15...)
Recommended reading:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
(Sorry for the terse answer...)
Well, I can't see your data, but I'm guessing that my_field doesn't exactly equal 0.15. Try:
select * from my_table where round(my_field::numeric,2) = 0.15;
Considering both PPTerka's and Jack's answer.
Approximate numeric data types do not store the exact values specified for many numbers;
Look here for MS' decription of real values.
http://technet.microsoft.com/en-us/library/ms187912(v=sql.105).aspx

Use max in a VARCHAR to get result > 999999?

What if somebody made a column as VARCHAR2(256 CHAR) and there are only numbers in this column. I would like to get the highest number. The problem is: the number is something > 999999 but a Max to a varchar is always giving me a max number of 999999
I tried to_number(max(numbers), '9999999999999') but i still get 999999 back, at that cant be. Any ideas? Thank you
the best way is to
First Solution
convert the column in numeric
or
Second Solution
convert data in you query in numeric and than get data...
Example
select max(col1) from(
select to_number(numbers) as col1 from table ) d
It has to be this way because if you call MAX() before TO_NUMBER(), it will sort alphabetically, and then 999999 is bigger than 100000000000. Note that applying TO_NUMBER() to a varchar2 column incurs the risk of an INVALID_NUMBER exception, should the column containing any non-numeric characters. This is why the first proposed solution is to be preferred.
In Oracle, the NUMBER type contains base 100 floating point values which have a precision of 38 significant digits, and a max value of 9999...(38 9's) x 10^125. There are two questions at issue - the first is whether a NUMBER can contain a value converted from a 256 character string, and the second is if two such values which are 'close' in numeric terms can be distinguished.
Let's start with taking a 256 character string and trying to convert it to a number. The obvious thing to do is:
SELECT TO_NUMBER('9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
Executing the above we get:
ORA-01426: numeric overflow
which, having paid attention earlier, we expected. The largest exponent that a NUMBER can handle is 125 - and here we're trying to convert a value with 256 significant digits. NUMBER's can't handle this. If we cut the number of digits down to 125, as follows:
SELECT TO_NUMBER('99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
It works fine, and our answer it 1E125.
<blink>
WHOA! WAIT!! WHAT??? The answer is 1 x 10^125??? What about all those 9's?!?!?!?
Remember earlier I'd mentioned that an Oracle NUMBER is a floating point value with a maximum precision of 38 and a maximum exponent of 125. From the point of view of TO_NUMBER 125 9's all strung together can't be exactly represented - too many digits (remember, max. precision of 38 (more on this later)). So it does the absolute best it can - it converts the first 38 digits (all of which are 9's) and then says "How should I best round this off to make the result A) representative of the input and B) as close as I can get to what I was given?". In this case it looks at digit 39, sees that it's a 9, and decides to round upward. As all the other digits are also 9's, it continues rounding neatly until it ends up with 1 as the remaining mantissa digit.
* Later, back at the ranch... *
OK, earlier I'd mentioned that NUMBER has a precision of 38 digits. That's not entirely true - it can actually differentiate between values with up to 40 digits of precision, at least sometimes, if the wind is right, and you're going downhill. Here's an example:
SELECT CASE
WHEN to_number('9999999999999999999999999999999999999999') >
to_number('9999999999999999999999999999999999999998')
THEN 'Greater'
ELSE 'Not greater'
END AS VAL
FROM DUAL;
Those two values each have 40 digits (counting is left as an exercise to the extremely bored reader :-). If you execute the above you'll get back 'Greater', showing that the comparison of two 40 digit values succeeded.
Now for some fun. If you add an additional '9' to each string, making for a 41 digit value, and re-execute the statement it'll return 'Not greater'.
<blink>
WAIT! WHAT?? WHOA!!! Those values are obviously different! Even a TotalFool (tm) can see that!!
The problem here is that a 41 digit number exceeds the precision of the NUMBER type, and thus when TO_NUMBER finds it has a value this long it starts discarding digits on the right side. Thus, even though those two really big numbers are clearly different to you and me, they're not different at all once they've been folded, spindled, mutilated, and converted.
So, what are the takeaways here?
1 - To the OP's original question - you'll have to come up with another way to compare your number strings besides using NUMBER because Oracle's NUMBER type can't hold 256 digit values. I suggest that you normalize the strings by making sure ALL the values are 256 digits long, adding zeroes on the left as needed, and then a string comparison should work OK.
2 - Floating point numbers prove the existence of (your favorite deity/deities here) by negation, as they are clearly the work of (your favorite personification of evil here). Whenever you work with them (as we all have to, sooner or later) you should remember that they are the foul byproducts of malignant evil, waiting to lash out at you when you least expect it.
3 - There is NO point three! (And extra credit for those who can identify without resorting to an extra-cranial search engine where this comes from :-)
Share and enjoy.
If you mean that the numbers in the column can be that big (256 digits), you could try something like this:
SELECT numbers
FROM (
SELECT numbers
FROM table_name
ORDER BY LPAD(numbers, 256) DESC
)
WHERE rownum = 1
or like this:
SELECT LTRIM(MAX(LPAD(numbers, 256))) AS numbers
FROM table_name