Unable to set precision for INTEGER data type in SQL CREATE TABLE command - oracle

I am trying to create the following table in Oracle.
CREATE TABLE CUSTOMER(CUST_ID INT(10),
CUST_NAME VARCHAR2(50),
CUST_SEX CHAR(2),
CUST_STATE VARCHAR2(50),
CUST_COUNTRY VARCHAR2(50));
I get an error saying that the right parenthesis is missing. In reality, the issue is with the INT data type for the CUST_ID column. Once I remove the precision :(10) from the DDL query, I am able to execute it successfully.
Oracle docs don't specify anything with regarding to whether this data type can be accompanied by a precision parameter or not. However Oracle does mention that INTEGER/INT is per ANSI standards.
https://docs.oracle.com/cd/B19306_01/olap.102/b14346/dml_datatypes002.htm
Certain other non-official references describe INT/INTEGER to be a synonym for NUMBER(38).
Can someone please tell me if precision cannot indeed be specified for INT datatype?

The Oracle docs state that:
SQL statements that create tables and clusters can also use ANSI data types and data types from the IBM products SQL/DS and DB2. Oracle recognizes the ANSI or IBM data type name that differs from the Oracle Database data type name. It converts the data type to the equivalent Oracle data type
As the table below that sentence states, int, integer, and (surprisingly?) smallint are all synonyms for number(38), so you cannot specify a precision for them. For your usecase, if you want an integer number with ten digits, you should use number(10).

Let me try: precision cannot indeed be specified for INT datatype.
How did it sound?
Documentation says:
<snip>
| { NUMERIC | DECIMAL | DEC } [ (precision [, scale ]) ] --> precision + scale
| { INTEGER | INT | SMALLINT } --> no precision for integers
| FLOAT [ (size) ]
<snip>

The INT[EGER] data type (which should be , at least mostly, a 4-byte binary integer), in Oracle, exists, if at all, in PL/SQL stored procedures.
Your best bet is to design a NUMBER(5) for a SMALLINT, a NUMBER(9) for an INTEGER, and a NUMBER(18) for a LARGEINT/BIGINT
If you go:
CREATE TABLE dropme (i INT);
, in Oracle, you get a table with a column i NUMBER (with no length specification, which boils down to a pretty in-efficient NUMBER(38).
The Oracle numeric data types are NUMBER , with an optional overall precision and an optional decimal scale, and FLOAT.
And an Oracle NUMBER, at least as I understood it, is a variable-length construct, with a binary, two-byte, length indicator for the whole thing, followed by a binary decimal notation , in which every following half-byte can hold between 0000 and 1001, binary, or 0 to 9 - except the last one, which contains the sign: positive/negative.

As the documentation says, INTEGER is equivalent to NUMBER(38).
You can just use INTEGER where you want to store integers of any size, or you can use NUMBER(n) if you want to constrain the number of digits in the values to n.
Note: the only reason for specifying the precision in Oracle is for validation and documentation. There is no space advantage in using smaller values of n: the value 123456 occupies the same number of bytes in NUMBER(6) and NUMBER(38) and INTEGER columns - i.e. 4 bytes.

Related

When to use RAW datatype column over VARCHAR2 in Oracle?

I've been working in a large scale project where all the primary keys are stored as RAW type. The ID field is auto-generated as a unique 16 digit UUID. I can't find any particular advantage of using RAW type column. Can someone help understand if there is any real advantage of storing primary keys in RAW format instead of VARCHAR2?
A GUID in Oracle is represented as raw(16).
You can get a GUID like this:
select sys_guid() from dual;
That's why you should use raw(16).
Well in the database design typically the size matter. The bigger key takes more space in storage, on disc, the sorting takes longer time etc.
From this point the integer database key is the most compact one (implemented as a NUMBER type with zero precision, allocation typically between 2-8 bytes).
From various reasons UUID is used as a key – with various motivations that are often independent of the database design rules.
Additionally, the UUID is often stored as formatted string in a VARCHAR2 column.
This is similar design as if you would store DATEs as a string (which is considered not a best practice).
Despite of it the RAW(16) columns allocate 16 bytes, the formatted UUID 36 bytes.
So in summary IMO there a following recommendations
Use NUMBER keys
If you can’t (and have solid arguments for it) use UUID in RAW(16) format
Note that of course the RAW format is a bit inconvenient to handle than a string (e.g. in setting of a bind variable). This often leads to the decision of storing the UUIDas a string - the vast majority of cases I encountered.
Below a small example illustrating the difference in sizing
create table tab
(id INT,
RAW_UUID RAW(16)
);
insert into tab(ID,RAW_UUID) values (1,sys_guid());
insert into tab(ID,RAW_UUID) values (1000000001,sys_guid());
select * from tab;
ID RAW_UUID
---------- --------------------------------
1 8135869AECF44FB280A04033888FD518
1000000001 DE04ED07DDD84D1AABE9059F38364C7E
select vsize(id), vsize(raw_uuid) from tab;
VSIZE(ID) VSIZE(RAW_UUID)
---------- ---------------
2 16
6 16
What you can do is to define a virtual column (i.e. column that allocates no space) that presents the formatted UUID:
alter table tab add ( UUID VARCHAR2(36) GENERATED ALWAYS AS
(SUBSTR(LOWER(RAWTOHEX(RAW_UUID)),1,8)||'-'||SUBSTR(LOWER(RAWTOHEX(RAW_UUID)),9,4)||'-'||
SUBSTR(LOWER(RAWTOHEX(RAW_UUID)),13,4)||'-'||SUBSTR(LOWER(RAWTOHEX(RAW_UUID)),17,4)||'-'||
SUBSTR(LOWER(RAWTOHEX(RAW_UUID)),21,12)) VIRTUAL VISIBLE);
Now the table has the text form UUID as well and you can use the familiar query
select * from tab where uuid = 'cbf7e2e2-a9e9-40fb-badc-18cb9a4fe663';
You can even define an index on the virtual column, but always before using UUID think on the Rule 1 above.

Different matches when using prepared statements on CHAR(3) column

I had to make a CHAR(1 CHAR) column wider and I forgot to change the column type to VARCHAR2:
DUPLICADO CHAR(3 CHAR)
I noticed the error when my PHP app would no longer find exact matches, e.g.:
SELECT *
FROM NUMEROS
WHERE DUPLICADO = :foo
... with :foo being #4 didn't find the 3-char padded #4 value. However, I initially hit a red herring while debugging the query in SQL Developer because injecting raw values into the query would find matches!
SELECT *
FROM NUMEROS
WHERE DUPLICADO = '#4'
Why do I get matches with the second query? Why do prepared statements make a difference?
To expand a little on my comments, I found a bit in the documentation that explains difference between blankpadded and nonpadded comparison:
http://docs.oracle.com/database/121/SQLRF/sql_elements002.htm#BABJBDGB
If both values in your comparison (the two sides of the equal sign) have datatype CHAR or NCHAR or are literal strings, then Oracle chooses blankpadded comparison. That means that if the lengths are different, then it pads the short one with blanks until they are the same length.
With the column DUPLICADO being a CHAR(3), the value '#4' is stored in the column as three characters '#4 ' (note the blank as third character.) When you do DUPLICADO = '#4' the rule states Oracle will use blankpadded comparison and therefore blankpad the literal '#4' until it has the same length as the column. So it actually becomes DUPLICADO = '#4 '.
But when you do DUPLICADO = :foo, it will depend on the datatype of the bind variable. If the datatype is CHAR, it will also perform blankpadded comparison. But if the datatype is VARCHAR2, then Oracle will use non-padded comparison and then it will be up to you to ensure to do blankpadding where necessary.
Depending on client or client language you may be able to specify the datatype of the bind variable and thereby get blankpadded or nonpadded comparison as needed.
SQL Developer may be a special case that might not allow you to specify datatype - it just possibly might default to bind variables always being datatype VARCHAR2. I don't know sufficient about SQL Developer to be certain about that ;-)

decimal(s,p) or number(s,p)?

recently, while working on a db2 -> oracle migration project, we came across this situation.
the developers were inadvertently creating new table structures using decimal(s,p) columns. I didn't remember Oracle supporting this, but then some digging showed that its a ANSI data type therefore supported by oracle.
However, question for me remained -
how is this data handled internally ?
is there a cost of using ANSI types instead of Oracle's built in types ?
Will there be an impact during the data migration if the target type was Oracle built-in type ?
In Oracle, they are the same:
SQL statements that create tables and clusters can also use ANSI data
types and data types from the IBM products SQL/DS and DB2. Oracle
recognizes the ANSI or IBM data type name that differs from the Oracle
Database data type name. It converts the data type to the equivalent
Oracle data type, records the Oracle data type as the name of the
column data type, and stores the column data in the Oracle data type
based on the conversions shown in the tables that follow.
The table below this quote shows that DECIMAL(p,s) is treated internally as a NUMBER(p,s):
SQL> create table t (a decimal(*,5), b number (*, 5));
Table created
SQL> desc t;
Name Type Nullable Default Comments
---- ----------- -------- ------- --------
A NUMBER(*,5) Y
B NUMBER(*,5) Y
However, the scale defaults to 0 for DECIMAL, which means that DECIMAL(*) is treated as NUMBER(*, 0), i.e. INTEGER:
SQL> create table t (a decimal, b number, c decimal (5), d decimal (5));
Table created
SQL> desc t;
Name Type Nullable Default Comments
---- --------- -------- ------- --------
A INTEGER Y
B NUMBER Y
C NUMBER(5) Y
D NUMBER(5) Y
Actually, there is difference between decimal and number.
Decimal will truncate the value which is over-scale, number will round the value.

Oracle - Cast Varchar to Float and specify the precision

I need to cast a varchar to a float. (The varchar is guaranteed to be a number)
I'm trying to create a materialized view on top of a pre-built table. Because of this, all the data types must match exactly...including the precision and size of the data types. The original column (before the NVL was added) was pulling from a FLOAT data type with a precision of 126. When I try casting a varchar to a float of a precision of 126 it doesn't seem to include the 126 data precision.
(I tested the fact that it wasn't including the 126 data size by creating a standard view with the below query that casts to float(126). Through my Toad IDE I could see that the precision of the "ThisorThat" float column was 38).
I have simply updated my materialized view with a NVL statement like so...
Select Cast(NVL(thisFloat, thatFloat) as Float(126)) as ThisorThat
....
From tables;
I get the error "Ora-12060: shape of prebuilt table does not match definition query" because the sizes are different than the original table that I am "pre-building" upon. I need to somehow cast the varchar to a float with an explicit size of 126. Any ideas?
Version: Oracle 10g
Edit:
Here's a link which is basically the same thing I'm encountering.
Use
TO_BINARY_FLOAT(mynumberstring)
or
TO_BINARY_DOUBLE(mynumberstring)
What you're trying to do actually isn't a cast, it's a conversion.

What is the difference between varchar and varchar2 in Oracle?

What is the difference between varchar and varchar2?
As for now, they are synonyms.
VARCHAR is reserved by Oracle to support distinction between NULL and empty string in future, as ANSI standard prescribes.
VARCHAR2 does not distinguish between a NULL and empty string, and never will.
If you rely on empty string and NULL being the same thing, you should use VARCHAR2.
Currently VARCHAR behaves exactly the same as VARCHAR2. However, the type VARCHAR should not be used as it is reserved for future usage.
Taken from: Difference Between CHAR, VARCHAR, VARCHAR2
Taken from the latest stable Oracle production version 12.2:
Data Types
The major difference is that VARCHAR2 is an internal data type and VARCHAR is an external data type. So we need to understand the difference between an internal and external data type...
Inside a database, values are stored in columns in tables. Internally, Oracle represents data in particular formats known as internal data types.
In general, OCI (Oracle Call Interface) applications do not work with internal data type representations of data, but with host language data types that are predefined by the language in which they are written. When data is transferred between an OCI client application and a database table, the OCI libraries convert the data between internal data types and external data types.
External types provide a convenience for the programmer by making it possible to work with host language types instead of proprietary data formats. OCI can perform a wide range of data type conversions when transferring data between an Oracle database and an OCI application. There are more OCI external data types than Oracle internal data types.
The VARCHAR2 data type is a variable-length string of characters with a maximum length of 4000 bytes. If the init.ora parameter max_string_size is default, the maximum length of a VARCHAR2 can be 4000 bytes. If the init.ora parameter max_string_size = extended, the maximum length of a VARCHAR2 can be 32767 bytes
The VARCHAR data type stores character strings of varying length. The first 2 bytes contain the length of the character string, and the remaining bytes contain the string. The specified length of the string in a bind or a define call must include the two length bytes, so the largest VARCHAR string that can be received or sent is 65533 bytes long, not 65535.
A quick test in a 12.2 database suggests that as an internal data type, Oracle still treats a VARCHAR as a pseudotype for VARCHAR2. It is NOT a SYNONYM which is an actual object type in Oracle.
SQL> select substr(banner,1,80) from v$version where rownum=1;
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
SQL> create table test (my_char varchar(20));
Table created.
SQL> desc test
Name Null? Type
MY_CHAR VARCHAR2(20)
There are also some implications of VARCHAR for ProC/C++ Precompiler options. For programmers who are interested, the link is at: Pro*C/C++ Programmer's Guide
After some experimentation (see below), I can confirm that as of September 2017, nothing has changed with regards to the functionality described in the accepted answer:-
Rextester demo for Oracle 11g:
Empty strings are inserted as NULLs for both VARCHAR
and VARCHAR2.
LiveSQL demo for Oracle 12c: Same results.
The historical reason for these two keywords is explained well in an answer to a different question.
VARCHAR can store up to 2000 bytes of characters while VARCHAR2 can store up to 4000 bytes of characters.
If we declare datatype as VARCHAR then it will occupy space for NULL values. In the case of VARCHAR2 datatype, it will not occupy any space for NULL values. e.g.,
name varchar(10)
will reserve 6 bytes of memory even if the name is 'Ravi__', whereas
name varchar2(10)
will reserve space according to the length of the input string. e.g., 4 bytes of memory for 'Ravi__'.
Here, _ represents NULL.
NOTE: varchar will reserve space for null values and varchar2 will not reserve any space for null values.
Currently, they are the same. but previously
Somewhere on the net, I read that,
VARCHAR is reserved by Oracle to support distinction between NULL and empty string in future, as ANSI standard prescribes.
VARCHAR2 does not distinguish between a NULL and empty string, and never will.
Also,
Emp_name varchar(10) - if you enter value less than 10 digits then remaining space cannot be deleted. it used total of 10 spaces.
Emp_name varchar2(10) - if you enter value less than 10 digits then remaining space is automatically deleted

Resources