Why does Oracle round up a number with less than 38 significant digits? - oracle

We have Oracle Server 10.2.
To test this, I have a very simple table.
CREATE TABLE MYSCHEMA.TESTNUMBER
(
TESTNUMBER NUMBER
)
When I try to insert 0.98692326671601283 the number gets rounded up.
INSERT INTO MYSCHEMA.TESTNUMBER (TESTNUMBER)
VALUES (0.98692326671601283);
The select returns:
select * from TESTNUMBER
0.986923266716013
It rounds up the last 3 numbers "283" to "3".
Even looking at it with TOAD UI and trying to enter it with TOAD, I get the same result.
Why? Is it possible to insert this number in an Oracle number without it getting rounded up?

I think you need to look into how your client program displays number values. An Oracle NUMBER should store that value with full precision; but the value may be rounded for display by the client.
For instance, using SQLPlus:
dev> create table dctest (x number);
Table created.
dev> insert into dctest VALUES (0.98692326671601283);
1 row created.
dev> select * from dctest;
X
----------
.986923267
dev> column x format 0.000000000000000000000000000
dev> /
X
------------------------------
0.986923266716012830000000000
As you can see, the default format shows only the first 9 significant digits. But when I explicitly change the column formatting (a client-side feature in SQLPlus), the full value inserted is displayed.

Related

Insertion of characters into number column

I have a table with several number columns that are inserted through a Asp.Net application using bind variables.
Due to upgrade of Oracle client to 19c and server change, the code instead of giving an error on insert of invalid data, inserts trash and the application crashes aftewards.
Any help is appreciated in finding the root cause.
SELECT trial1,
DUMP (trial1, 17),
DUMP (trial1, 1016),
trial3,
DUMP (trial3,17),
DUMP (trial3, 1016)
Result in SQL Navigator
results of query
Oracle 12c
Oracle client 19
My DBA found this on Oracle Support and that lead to us find the error in the application side:
NaN is a specific IEEE754 value. However Oracle NUMBER is not IEEE754
compliant. Therefore if you force the data representing NaN into a
NUMBER column results are unpredicatable. SOLUTION If you can put a
value in a C float, double, int etc you can load this into the
database as no checks are undertaken - just as with the Oracle NUMBER
datatype it's up to the application to ensure the data is valid. If
you use the proper IEEE754 compliant type, eg BINARY_FLOAT, then NaN
is recognised and handled correctly.
You have bad data as you have tried to store an double precision NAN value in a NUMBER column rather than a BINARY_DOUBLE column.
We can duplicate the bad data with the function (never use this in a production environment):
CREATE FUNCTION createNumber(
hex VARCHAR2
) RETURN NUMBER DETERMINISTIC
IS
n NUMBER;
BEGIN
DBMS_STATS.CONVERT_RAW_VALUE( HEXTORAW( hex ), n );
RETURN n;
END;
/
Then, we can duplicate your bad values using the hexadecimal values from your DUMP output:
CREATE TABLE table_name (trial1 NUMBER, trial3 NUMBER);
INSERT INTO table_name (trial1, trial3) VALUES (
createNumber('FF65'),
createNumber('FFF8000000000000')
);
Then:
SELECT trial1,
DUMP(trial1, 16) AS t1_hexdump,
trial3,
DUMP(trial3, 16) AS t3_hexdump
FROM table_name;
Replicates your output:
TRIAL1
T1_HEXDUMP
TRIAL3
T3_HEXDUMP
~
Typ=2 Len=2: ff,65
null
Typ=2 Len=8: ff,f8,0,0,0,0,0,0
Any help is appreciated in finding the root cause.
You need to go back through your application and work out where the bad data came from and see if you can determine what the original data was and debug the steps it went through in the application to work out if it was:
Always bad data, and then you need to put in some validation into your application to make sure the bad data does not get propagated; or
Was good data but there is a bug in your code that changed it and then you need to fix the bug.
As for the existing bad data, you either need to correct it (if you know what it should be) or delete it.
We cannot help with any of that as we do not have visibility of your application nor do we know what the correct data should have been.
If you want to store that data as a floating point then you need to change from using a NUMBER to using a BINARY_DOUBLE data type:
CREATE TABLE table_name (value BINARY_DOUBLE);
INSERT INTO table_name(value) VALUES (BINARY_DOUBLE_INFINITY);
INSERT INTO table_name(value) VALUES (BINARY_DOUBLE_NAN);
Then:
SELECT value,
DUMP(value, 16)
FROM table_name;
Outputs:
VALUE
DUMP(VALUE,16)
Inf
Typ=101 Len=8: ff,f0,0,0,0,0,0,0
Nan
Typ=101 Len=8: ff,f8,0,0,0,0,0,0
Then BINARY_DOUBLE_NAN exactly matches the binary value in your column and you have tried to insert a Not-A-Number value into a NUMBER column (that does not support it) in the format expected for a BINARY_DOUBLE column (that would support it).
The issue was a division by zero on the application side that was inserted as infinity into the database, but Oracle has an unpredictable behavior with this values.
Please see original post above for all the details.

How to spool a number in oracle in correct format

I am trying to spool the records by executing script in SqlDeveloper, everything is working fine except printing the record of columns which are NUMBER(38,0) data type.
All numbers record are printing in below format:
1.5E+18
I tried many combinations by using set but nothing working.
You can use to_char; for example:
SQL> select to_char(n, '999999999999999999999') from numbers;
TO_CHAR(N,'99999999999
----------------------
1500000000000000000
This way you can decide the exact format you want to use; for example:
SQL> select to_char(n, 'FM999G999G999G999G999G999G999G999') from numbers;
TO_CHAR(N,'FM999G999G999G999G999
--------------------------------
1.500.000.000.000.000.000
If you don't need a full control on the format, and/or you don't want to chenge your code, you can follow the suggestion by Nicholas Krasnov :
SQL> set numwidth 50
SQL> select n from numbers;
N
--------------------------------------------------
1500000000000000000

Oracle truncating column

I have the following query.
insert into ORDER_INFO(ORDINF_PK,ORDINF_LGNDET_PK_FK,MEDIA_TYPE,ORDINF_MUSIC_FK,DAT)
values (1,1,'Music',21,TO_DATE('14-OCT-2015','DD-MON-YYYY'));
insert into ORDER_INFO(ORDINF_PK,ORDINF_LGNDET_PK_FK,MEDIA_TYPE,ORDINF_MUSIC_FK,ORDINF_SERIES_FK,DAT)
values (2,2,'Series',71,23,TO_DATE('07-NOV-2015','DD-MON-YYYY'));
however when I do:
select * from ORDER_INFO;
I get:
truncating (as requested) before column ORDINF_SERIES_FK
truncating (as requested) before column ORDINF_MOVIES_FK
ORDINF_PK ORDINF_LGNDET_PK_FK MEDIA_TYPE ORDINF_MUSIC_FK DAT
---------- ------------------- -------------------- --------------- ---------
1 1 Music 21 14-NOV-14
2 2 Series 71 07-NOV-15
I understand that it is truncating ORDINF_MOVIES_FK because there is no entry in that column, but why is it truncating the column ORDINF_SERIES_FK?
I managed to solve the issue, I did this.
set wrap on;
set pagesize 50000;
set linesize 120;
link here: http://www.anattatechnologies.com/q/2012/01/sqlplus-pagesize-and-linesize/
Pay attention - both rows inserted and ARE in the database, so INSERT succeeded.
The warning you get is a warning from SQLPlus program that means that column is display not with full width (maybe the definition of column is long and SQLPlus decides to show column shorter because data you have in there is short).
You do not need to worry about this in any case.
See this link for more explanation about SQL*Plus wrap.

How MAX of a concatenated column in oracle works?

In Oracle, while trying to concatenate two columns of both Number type and then trying to take MAX of it, I am having a question.
i.e column A column B of Number data type,
Select MAX(A||B) from table
Table data
A B
20150501 95906
20150501 161938
when I’m running the query Select MAX(A||B) from table
O/P - 2015050195906
Ideally 20150501161938 should be the output????
I am trying to format column B like TO_CHAR(B,'FM000000') and execute i'm getting the expected output.
Select MAX(A || TO_CHAR(B,'FM000000')) FROM table
O/P - 2015011161938
Why is 2015050195906 is considered as MAX in first case.
Presumably, column A is a date and column B is a time.
If that's true, treat them as such:
select max(to_date(to_char(a)||to_char(b,'FM000000'),'YYYYMMDDHH24MISS')) from your_table;
That will add a leading space for the time component (if necessary) then concatenate the columns into a string, which is then passed to the to_date function, and then the max function will treat as a DATE datatype, which is presumably what you want.
PS: The real solution here, is to fix your data model. Don't store dates and times as numbers. In addition to sorting issues like this, the optimizer can get confused. (If you store a date as a number, how can the optimizer know that '20141231' will immediately be followed by '20150101'?)
You should convert to number;
select MAX(TO_NUMBER(A||B)) from table
Concatenation will result in a character/text output. As such, it sorts alphabetically, so 9 appears after 16.
In the second case, you are specifiying a format to pad the number to six digits. That works well, because 095906 will now appear before 161938.

Oracle: Coercing VARCHAR2 and CLOB to the same type without truncation

In an app that supports MS SQL Server, MySQL, and Oracle, there's a table with the following relevant columns (types shown here are for Oracle):
ShortText VARCHAR2(1700) indexed
LongText CLOB
The app stores values 850 characters or less in ShortText, and longer ones in LongText. I need to create a view that returns that data, whichever column it's in. This works for SQL Server and MySQL:
SELECT
CASE
WHEN ShortText IS NOT NULL THEN ShortText
ELSE LongText
END AS TheValue
FROM MyTable
However, on Oracle, it generates this error:
ORA-00932: inconsistent datatypes: expected CHAR got CLOB
...meaning that Oracle won't implicitly convert the two columns to the same type, so the query has to do it explicitly. Don't want data to get truncated, so the type used has to be able to hold as much data as a CLOB, which as I understand it (not an Oracle expert) means CLOB, only, no other choices are available.
This works on Oracle:
SELECT
CASE
WHEN ShortText IS NOT NULL THEN TO_CLOB(ShortText)
ELSE LongText
END AS TheValue
FROM MyTable
However, performance is amazingly awful. A query that returns LongText directly took 70-80 ms for about 9k rows, but the above construct took between 30 and 60 seconds, unacceptable.
So:
Are there any other Oracle types I could coerce both columns to
that can hold as much data as a CLOB? Ideally something more
text-oriented, like MySQL's LONGTEXT, or SQL Server's NTEXT (or even
better, NVARCHAR(MAX))?
Any other approaches I should be looking at?
Some specifics, in particular ones requested by #Guido Leenders:
Oracle version: Oracle Database 11g 11.2.0.1.0 64bit Production
Not certain if I was the only user, but the relative times are still striking.
Stats for the small table where I saw the performance I posted earlier:
rowcount: 9,237
varchar column total length: 148,516
clob column total length: 227,020
The to_clob is pretty expensive, so try to avoid it. But I think it should perform reasonable well for 9K rows. Following test case based upon one of the applications we develop which has the similar datamodel behaviour:
create table bubs_projecten_sample
( id number
, toelichting varchar2(1700)
, toelichting_l clob
)
begin
for i in 1..10000
loop
insert into bubs_projecten_sample
( id
, toelichting
, toelichting_l
)
values
( i
, case when mod(i, 2) = 0 then 'short' else null end
, case when mod(i, 2) = 0 then rpad('long', i, '*') else null end
)
;
end loop;
commit;
end;
Now make sure everything in cache and dirty blocks written out:
select *
from bubs_projecten_sample
Test performance:
create table bubs_projecten_flat
as
select id
, to_clob(toelichting) toelichting_any
from bubs_projecten_sample
where toelichting is not null
union all
select id
, toelichting_l
from bubs_projecten_sample
where toelichting_l is not null
The create table take less than 1 second on a normal entry level server, including writing out the data, 17K consistent gets, 4K physical reads. Stored on disk (note the rpad) is 25K for toelichting and 16M for toelichting_l.
Can you further elaborate on the problem?
Please check that large CLOBs are not stored inline. Normally large CLOBs are stored in a separate system-maintained table. Storing large CLOBs inside a table can make going through the table with a Full Table Scan expensive.
Also, I can imagine populating both columns always. You still have the benefits of indexing working for the first so many characters. You just need to memorize in the table using an indicator whether the CLOB or the shortText column is leading.
As a side note; I see a difference between 850 and 1700. I would recommend making them equal, but remember to check that you are creating the table using character semantics. That can be done on statement level by using: "varchar2(850 char)". Please note that Oracle will actually create a column that fits 850 * 4 bytes (in AL32UTF8 at least, there the "32" stands for "4 bytes at most per character"). Good luck!

Resources