I have created CHECK constraint but receiving error message - sql

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).

Related

How to always keep a number with 2 decimal places?

I'm trying to keep the numbers in a column in sqlite to have two decimal places after the comma. If there is only one number after the decimal point, then I must add 0. Example:
drop table if exists numbers;
create table numbers (number float);
insert into numbers values ('97.516,82');
insert into numbers values ('97.409,9');
insert into numbers values ('97.409,87');
insert into numbers values ('97.331,6'); -- correct 97.331,60
insert into numbers values ('97.229,00');
insert into numbers values ('97.183,70');
insert into numbers values ('97.163,25');
insert into numbers values ('97.163,1'); -- correct 97.163,10
insert into numbers values ('97.121,98');
insert into numbers values ('97.105,56');
insert into numbers values ('97.101,44');
insert into numbers values ('97.076,77');
insert into numbers values ('97.060,28');
insert into numbers values ('97.019,17');
insert into numbers values ('97,34');
insert into numbers values ('97,3'); -- correct 97,30
insert into numbers values ('969,19');
insert into numbers values ('968,89');
insert into numbers values ('968,7'); -- correct 968,70
insert into numbers values ('968,76');
select * from numbers
I commented the way it should be formatted, I already tried the following
select printf('%.2f',number) from numbers
But it stays in this format that I don't want
Can anyone help?
printf('%.2f',number) is the correct way to always display two decimal places, but the problem is in the INSERT not in the query.
To get the correct result in the query you need to correct the values you insert.
Sqlite has Flexible Data Typing. Since you declared the column as FLOAT, it will try to convert any value you try to insert to an INTEGER or REAL (in order of preference).
Since in every value a , character is present, Sqlite will interpret it as a string and store it as a TEXT value.
When you try to convert to a floating point with printf('%.2f',number), the point is interpreted as decimal separator and anything after a comma , is discarded, so the first value you insert is converted to 97.516 (which is then rounded by printf('%.2f',number) to 97.52) while the last is converted to 968 (and printed as 968.00).
printf('%.2f',replace(replace(number,'.',''),',','.') is a good way to do this, but you can't add thousands separators. To add them you need to concatenate the integer part and the decimal part as if they where two integers:
select printf('%,d.%02d',replace(replace(number,'.',''),',','.'),replace(replace(number,'.',''),',','.')*100%100) from numbers
The first value will be converted to an integer with thousand separators, then a point and then the second number (the first two digits of the decimal parts treated as an integer).
Step 1: Identify the digit at the thousandths place or the third place to the right of the decimal point.
Step 2: If the thousandths place digit is greater than or equal to 5, the hundredths place digit will be increased by one. And if the thousandths place digit is less than 5, the hundredths place digit will remain unchanged.
Step 3: Ignore all the remaining digits in the decimal part of the number.
Look at the image given below showing the rounding up value of a decimal number 2.835706 up to two decimal places.
round to two decimal places
Let us take one more example of round to two decimal places to understand this concept better. Round 5.803 to two decimal places.
To do this, let us follow the above steps. The first step is to identify the thousandths place digit which is 3 in this number. Now, since 3<5, we have to keep the digit at the hundredths place unchanged. Therefore, 5.803 ≈ 5.80 (where ≈ is the symbol for approximation).
Use Cuemath's free online round to two decimal places calculator to verify your answers.

I get this error: ORA-01438: value larger than specified precision allowed for this column

I have a column in my database table that is of type Number(5,3). I need to be able to insert data or update data to this column. I am currently using a form field that lets users input whatever number they want. This field is the one used when inserting or updating data into this column of type Number(5,3). When testing I enter any number and get this error: ORA-01438: value larger than specified precision allowed for this column
I am aware the data type NUMBER(5,3) means 5 is precision (total number of digits) and the 3 means scale (number of digits to the right of decimal point). For example: 52.904
Is there a function in oracle to format any number into a number of this type: NUMBER(5,3)?
Again I would like for the user to input any number on the field and be able to process that number as NUMBER(5,3) to insert or update into my table.
You could use something like this:
select cast (512.33333333 as number(5,2)) from dual;

How to specify min and max digits for a bank account number?

Is it possible to constrain the number of digits allowed in a column of integer data type in PostgreSQL. I have the following example:
CREATE TABLE bank_accounts (
id SERIAL PRIMARY KEY
, number_account INTEGER(26) NOT NULL
);
We can enter something like:
1 -- one digit
23 -- two digits
444 -- three digits
5555 -- four digits
Etc. ... up to 26 digits.
But I want to constrain my column to store exactly 26 digits, not less and not more. How to achieve that?
A bank account number is not an integer by nature. 26 decimal digits are too much for integer or bigint anyway.
A bank account number is not a numeric value at all, really, even if we could use the type numeric for storage. It can handle 26 decimal digits easily. But it also allows fractional digits (and other decorators, like #klin commented). You can restrict to numeric(26), which is short for numeric(26,0), to remove fractional digits from storage. But that still allows fractional digits on input, which are then rounded off. And other decorators. All of these seem undesirable for a bank account number:
SELECT numeric(26) '12345678901234567890123456'
, numeric(26) '12345678901234567890123456.4' -- rounded down
, numeric(26) '12345678901234567890123456.5' -- rounded up
, numeric(26) '1e25'
, numeric(26) '1.2345e25'
, numeric(26) '+12345678901234567890123456.5'
SELECT numeric(26) '99999999999999999999999999.5' -- error after rounding up
A bank account number is more like text by nature, so data type text seems more appropriate (like #klin provided), even if that occupies a bit more space on disk (like #a_horse mentioned). 27 bytes vs. 17 bytes for numeric - or 30 vs. 20 bytes in RAM. See:
What is the overhead for varchar(n)?
However, you would not want to apply collation rules to bank account numbers. That happens with collatable types like text or varchar if your DB cluster runs with a non-C locale. Would be a void effort for only digits to begin with. But you still get slower sorting and slower indexes etc. Notably, the "abbreviated keys" feature in Postgres 9.5 or later is currently (incl. Postgres 10) disabled for non-C locales.
Putting everything together, I suggest:
CREATE TABLE bank_account (
bank_account_id serial PRIMARY KEY
-- bank_account_id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY -- in Postgres 10+
, number_account text COLLATE "C" NOT NULL -- disable collation rules
, CONSTRAINT number_account_has_26_digits CHECK (number_account ~ '^\d{26}$')
);
Asides:
Consider an IDENTITY column instead of the serial in in Postgres 10+. Details:
https://blog.2ndquadrant.com/postgresql-10-identity-columns/
INTEGER(26) is not valid syntax in Postgres, where the integer data type has no modifiers. You can chose from int2, int4 (default integer) and int8, though - the dangling number signifying occupied bytes, not the number of digits allowed.
The maximum integer value is 2147483647, maximum bigint is 9223372036854775807. You cannot use integer types for the column.
It seems that the simplest way is to define the column as text with a check constraint:
CREATE TABLE bank_accounts (
id serial primary key,
number_account text not null check (number_account ~ '^\d{26}$')
);
The regular expression used in the check constraint means a string with exactly 26 digits.

Comparing 2 numbers with different precision and scale

I am writing a condition to check 2 columns both data type number.
for example varchar2(100) is larger then varchar2(50) because it holds more character. Now for numbers, number(9,1) is larger then number(10,3) because it hold more number ?
In the below example F_IDis a number(10,3) , I want to compare with another column which is number(9,3)
DECLARE COL_COUNT NUMBER;
OLD_PREC NUMBER(5);
OLD_SCALE NUMBER(5);
BEGIN
SELECT DATA_PRECISION, DATA_SCALE INTO OLD_PREC,OLD_SCALE FROM USER_TAB_COLS
WHERE TABLE_NAME='EX_EMPLOYEE' AND COLUMN_NAME='F_ID';
IF ( OLD_PREC <= 9 AND OLD_SCALE < 3 ) THEN
-- do something
Can I compare it like this? Because (9,1) = 12345678,1 and (10,3) = 123456,1234, so basically 9,1 is larger then 10,3 right?
Oracle stores numbers in scientific notation: http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CNCPT1833 so there is no direct relation between decimal precision and space it needs to store the number.
In case you want to check which one will take more space as a text why don't compare length(to_char(n1)) > length(to_char(n2)) ?
column_name NUMBER (precision, scale)
In the declaration above the precision is total number of digits and scale is number of digits to the right of the decimal point.
By subtracting the scale from precision you'll find the digits left of the decimal point.
Hence,
NUMBER (9,1) could contain maximum 8 length number left of the decimal point : 9999,9999.9
and
NUMBER (10,3) could contain maximum 7 length number left of the decimal point : 999,9999.999
Considering this you can say that first column is larger.
Just be aware that in case when the the digits right of the decimal point are equal you have to check the scale
NUMBER (8,1) could contain maximum 7 length number left of the decimal point: 999,9999.9
and
NUMBER (10,3) could contain maximum 7 length number left of the decimal point : 999,9999.999
But in this case the second column is larger because it has 3 length scale
You said:
"varchar2(100) is larger then varchar2(50) right ? it holds more
character . okay now for numbers, number(9,1) is larger then
number(10,3) "
As an infrence i can only get that you wanted to compare the data types. See below how you can do it. I uses a PLSQL anonymous block to explain.
declare
a number (9, 1);
b number (10, 3);
begin
if length (a) > length (b)
then
dbms_output.put_line ('a is bigger');
else
dbms_output.put_line ('a is smaller');
end if;
end;
Output:
SQL> /
a is smaller
PL/SQL procedure successfully completed.

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

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...