how to make distinct type with condition in oracle - oracle

create or replace TYPE PDV AS OBJECT
( percentage NUMBER(4,2),
MEMBER FUNCTION get_percentage RETURN NUMBER
) INSTANTIABLE NOT FINAL;
create or replace TYPE BODY PDV AS
MEMBER FUNCTION get_percentage RETURN NUMBER AS
BEGIN
return SELF.percentage;
END get_percentage;
END;
I have table Product (productID, name, description, percentage)
When I insert this in the database, it should be saved in the table Product:
insert into Product VALUES (1, 'Table', 'Brown table for six people.Made of oak', PDV(20.00));
When I insert this into the database, an error should occur:
insert into Product VALUES (1, 'Table', 'Brown table for six people.Made of oak', PDV(130.20));
I want to make distinct type with condition - percentage must be between 0 and 100. Where to put that condition?

percentage must be between 0 and 100. Where to put that condition?
Nowhere, I'd say. PERCENTAGE's data type, a numeric having precision 4 and scale 2 will take care about it.
As you didn't provide whole code you use, here's an example.
SQL> create or replace type pdv as object
2 (productid number,
3 name varchar2(30),
4 description varchar2(100),
5 percentage number(4, 2),
6 member function get_percentage return number
7 ) instantiable not final;
8 /
Type created.
SQL> create or replace type body pdv as
2 member function get_percentage return number as
3 begin
4 return self.percentage;
5 end get_percentage;
6 end;
7 /
Type body created.
SQL> create table product (prod pdv);
Table created.
SQL> insert into product values (pdv(1, 'Table', 'Made of oak', 20));
1 row created.
SQL> insert into product values (pdv(1, 'Table', 'Made of oak', 130));
insert into product values (pdv(1, 'Table', 'Made of oak', 130))
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into product values (pdv(1, 'Table', 'Made of oak', 55.567));
1 row created.
SQL>
[EDIT, written after reading OP's comment]
I'm not sure I understood what you mean. If it means that you want to create an inline CHECK constraint, well, that won't work:
SQL> create or replace type pdv_typ as object
2 (
3 producid number,
4 name varchar2 (30),
5 description varchar2 (100),
6 percentage number (4, 2) constraint ch_perc check (percentage between 0 and 100),
7 member function get_percentage
8 return number
9 );
10 /
create or replace type pdv_typ as object
*
ERROR at line 1:
ORA-06545: PL/SQL: compilation error - compilation aborted
ORA-06550: line 6, column 29:
PLS-00103: Encountered the symbol "CONSTRAINT" when expecting one of the
following:
:= ) , not null default external character
ORA-06550: line 0, column 0:
PLS-00565: PDV_TYP must be completed as a potential REF target (object type)
SQL>
What you could do is to create a table whose column data type is of a previously created type, such as this:
SQL> create or replace type pdv_typ as object
2 (
3 producid number,
4 name varchar2 (30),
5 description varchar2 (100),
6 percentage number (4, 2),
7 member function get_percentage
8 return number
9 );
10 /
Type created.
SQL> create or replace type body pdv_typ
2 as
3 member function get_percentage
4 return number
5 as
6 begin
7 return self.percentage;
8 end get_percentage;
9 end;
10 /
Type body created.
SQL> create table pdv_tab
2 (
3 id number primary key,
4 pdv pdv_typ constraint ch_perc check (pdv.percentage between 0 and 100)
5 );
Table created.
SQL>
SQL> insert into pdv_tab values (1, pdv_typ (1, 'Table', 'Made of oak', 20));
1 row created.
SQL> insert into pdv_tab values (2, pdv_typ (2, 'Chair', 'Made of steel', 120));
insert into pdv_tab values (2, pdv_typ (2, 'Chair', 'Made of steel', 120))
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into pdv_tab values (3, pdv_typ (3, 'Window', 'Made of glass', -5));
insert into pdv_tab values (3, pdv_typ (3, 'Window', 'Made of glass', -5))
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CH_PERC) violated
SQL>
Personally, I really, really don't like object side of Oracle. For me, it is a relational DBMS. Object support came as if they wanted to say hey, look, we can do that too!. I can't remember whether I ever used that functionality, except for educational purposes. Drawbacks? Plenty. Benefits? None. Once again, from my point of view.
So, what are you, really, doing? This is not a real-life problem, is it? That suggests your comment, saying that it has to be done that way. Says who? If it is a client, tell him to mind his own business. If it is a professor, then do what he says, regardless.

The straightforward way to do this would be a CHECK constraint on the table. That could be as simple as:
create table Product (
productID number primary key
, name varchar2(30) not null
, description varchar2(128) not null
, percentage number check (percentage between 0 and 100)
);
Demo in SQL Fiddle.

Related

How does Number data type work in Oracle 21c?

I created a table like this:
CREATE TABLE table(
id INTEGER GENERATED ALWAYS AS IDENTITY,
nome VARCHAR2(100 CHAR)
)
ALTER TABLE table ADD CONSTRAINT table_pk PRIMARY KEY (ID);
CREATE UNIQUE INDEX TABLE_UNIQ_IDX ON TABLE(NOME ASC);
ALTER TABLE table ADD (PERC NUMBER(1, 2) NOT NULL);
Then I tried to write 2 records on it:
INSERT INTO TABLE(NOME,PERC)VALUES('a',0.8);
INSERT INTO TABLE(NOME,PERC)VALUES('b',0.2);
Then I received this error:
ORA-01438: valor maior que a precisão especificada usado para esta coluna
Translated:
ORA-01438: value larger than specified precision allows for this column
I tried select cast (0.8 as number(1,1)) from dual; and it worked but when I tried select cast (0.8 as number(1,2)) from dual; I received the same error.
I then tried select cast (0.81 as number(1,2)) from dual; and received the same ORA-01438.
I changed my field to number(1,1), no big deal, but how does this "Number" data type work?
Shouldn't select cast (0.81 as number(1,2)) from dual; have worked?
Why does select cast (0.81 as number(2,2)) from dual; work and
select cast (0.81 as number(2,3)) from dual; does not?
Thanks for any help
If you have NUMBER(precision, scale) then precision is the number of digits and scale is the number of decimal places.
So, NUMBER(1, 2) has a single digit and 2 decimal places. The minimum value it can store is -0.09 and the maximum it can store is +0.09.
NUMBER(2,2) works as it stores 2 digits in 2 decimal places (from -0.99 to +0.99).
NUMBER(2,3) does not work as it stores 2 digits in 3 decimal places (from -0.099 to +0.099).
What you said, is that perc column should accept numeric values whose length is 1, and out of that 1, you want to keep 2 decimal places. That won't work.
SQL> create table test (perc number(1, 2));
Table created.
SQL> insert into test values (0.8);
insert into test values (0.8)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
Perhaps you meant to put it vice versa?
SQL> alter table test modify perc number(2, 1);
Table altered.
SQL> insert into test values (0.8);
1 row created.
SQL>

Query select on varchar2 works without apex

I have a query on field type varchar2 that works also without apex.
E.g.
SELECT * FROM table WHERE field = 123456
where field is varchar2, and it returns the result and not the
ORA-01722: invalid number.
There is some setting on DB that make it possible? Thankx.
If that column's datatype is varchar2, then enclose its value with single quotes:
SELECT * FROM table WHERE field = '123456'
There's no setting that would "remove" invalid number error, as far as I can tell.
Query you posted works when there are only "numbers" in a table; though, when "something else" (it is a varchar2 after all) is stored in the column, it won't work any more:
Sample table:
SQL> create table test (id number, field varchar2(10));
Table created.
Numbers only:
SQL> insert into test (id, field) values (1, 123456);
1 row created.
SQL> select * from test where field = 123456;
ID FIELD
---------- ----------
1 123456
Enter value that contains e.g. letters in field:
SQL> insert into test (id, field) values (2, 'ABC');
1 row created.
SQL> select * from test where field = 123456;
ERROR:
ORA-01722: invalid number
no rows selected
SQL>
So, it is not "Apex" to blame, but you who tried to misuse what you have.

How should I use an object type in an insert DML statement?

I have created two TYPE objects to try out OOP processing in PL/SQL.
I tried to use my type o_customers in my INSERT statement, but I could not do it.
There is a Customers table. It has same columns as o_customers.
create or replace type o_customers as object (
id number,
name varchar2(40),
age number,
address o_addressC,
salary number
);
create or replace type o_addressC as object (
mahalle varchar(30),
apartman varchar(15),
ilce varchar(15),
apt_no number
);
declare
adres o_addressC;
musteri o_customers;
begin
adres := o_addressC('selami ali mah','çınar apt',' üsküdar',19);
musteri:= o_customers(10,'UĞUR SİNAN SAĞIROĞLU',26,adres,1000);
insert into customers values (musteri);
end;
" There is a customers table. it has same columns with o_customers"
In OOP it is not enough for objects to have the same structure to be compatible in a programming context: they must be the same type, or related to each other through inheritance.
So you need to create the table using that type:
SQL> create table customers of o_customers
2 /
Table created.
SQL> desc customers
Name Null? Type
---------------------- -------- -------------
ID NUMBER
NAME VARCHAR2(40)
AGE NUMBER
ADDRESS O_ADDRESSC
SALARY NUMBER
SQL>
Now your insert statement will work:
SQL> declare
2 adres o_addressC;
3 musteri o_customers;
4 begin
5 adres := o_addressC('selami ali mah','cınar apt','uskudar',19);
6 musteri:= o_customers(10,'UĞUR SİNAN SAĞIROĞLU',26,adres,1000);
7 insert into customers values(musteri);
8 end;
9 /
PL/SQL procedure successfully completed.
SQL> select * from customers;
ID NAME AGE
---------- ---------------------------------------- ----------
ADDRESS(MAHALLE, APARTMAN, ILCE, APT_NO)
------------------------------------------------------------------------------------------------------------------------------------------------------
SALARY
----------
10 UĞUR SİNAN SAĞIROĞLU 26
O_ADDRESSC('selami ali mah', 'c??nar apt', ' uskudar', 19)
1000
SQL>
Incidentally I had to make minor changes to the inserted values because the posted statement hurled
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
This is because your o_addressC type attributes are too small for strings with multi-byte characters.
Unless customers is an object table (create table customers of o_customers), you'll need to refer to the object's properties explicitly:
insert into customers
( id, name, age, address, salary)
values
( musteri.id, musteri.name, musteri.age, musteri.address, musteri.salary );
By the way, o_customer (no 's') would make more sense than o_customers for an object name.

varrays oracle ORA-02330

I'm develop a simple app for learn about oracle and database object-relational with objects and varrays... I did the next code:
this is my varrays
SQL> create or replace type software_va as varray(3) of varchar2(30);
2 /
here is an object that I created:
SQL> create or replace type cargo1 as object(
2 id_cargo number,
3 nom_cargo varchar2(20),
4 suc ref sucursal);
5 /
when I try to create the table at this way:
SQL> create table cargos of cargo1(
2  primary key(id_cargo),
3  manejosoft software_va);
I got this error:
ERROR en line 3:
ORA-02330: datatype specification not allowed
I don't understand why I got this error and don't know if I have something wrong
If you want a relational table with both object and varray columns, this should work, and still has a primary key based on the object's ID:
create table cargos
(
cargo cargo1,
manejosoft software_va,
constraint cargos_pk primary key (cargo.id_cargo)
);
Table created.
insert into cargos values (cargo1(1, 'test'), software_va('a', 'b', 'c'));
1 row created.
insert into cargos values (cargo1(1, 'dup test'), software_va('d', 'e', 'f'));
insert into cargos values (cargo1(1, 'dup test'), software_va('d', 'e', 'f'))
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.CARGOS_PK) violated
select * from cargos;
CARGO(ID_CARGO, NOM_CARGO)
--------------------------------------------------------------------------------
MANEJOSOFT
--------------------------------------------------------------------------------
CARGO1(1, 'test')
SOFTWARE_VA('a', 'b', 'c')
select c.cargo.nom_cargo
from cargos c
where c.cargo.id_cargo = 1;
CARGO.NOM_CARGO
--------------------
test
If you wanted an object table then you couldn't have the varray column as mentioned in comments:
create table cargos of cargo1
(
primary key(id_cargo)
);

How to generate alphanumeric id in Oracle

In my vb application I want an autogenerated id of alphanumeric characters, like prd100. How can I increment it using Oracle as backend?
Any particular reason it needs to be alphanumeric? If it can just be a number, you can use an Oracle sequence.
But if you want just a random string, you could use the dbms_random function.
select dbms_random.string('U', 20) str from dual;
So you could probably combine these 2 ideas (in the code below, the sequence is called oid_seq):
SELECT dbms_random.string('U', 20) || '_' || to_char(oid_seq.nextval) FROM dual
There are two parts to your question. The first is how to create an alphanumeric key. The second is how to get the generated value.
So the first step is to determine the source of the alpha and the numeric components. In the following example I use the USER function and an Oracle sequence, but you will have your own rules. I put the code to assemble the key in a trigger which is called whenever a row is inserted.
SQL> create table t1 (pk_col varchar2(10) not null, create_date date)
2 /
Table created.
SQL> create or replace trigger t1_bir before insert on t1 for each row
2 declare
3 n pls_integer;
4 begin
5 select my_seq.nextval
6 into n
7 from dual;
8 :new.pk_col := user||trim(to_char(n));
9 end;
10 /
Trigger created.
SQL>
The second step requires using the RETURNING INTO clause to retrieve the generated key. I am using SQL*PLus for this example. I confess to having no idea how to wire this syntax into VB. Sorry.
SQL> var new_pk varchar2(10)
SQL> insert into t1 (create_date)
2 values (sysdate)
3 returning pk_col into :new_pk
4 /
1 row created.
SQL> print new_pk
NEW_PK
--------------------------------
APC61
SQL>
Finally, a word of warning.
Alphanumeric keys are a suspicious construct. They reek of "smart keys" which are, in fact, dumb. A smart key is a value which contains multiple parts. At somepoint you will find yourself wanting to retrieving all rows where the key starts with 'PRD', which means using SUBSTR() or LIKE. Even worse someday the definition of the smart key will change and you will have to cascade a complicated update to your table and its referencing foreign keys. A better ides is to use a surrogate key (number) and have the alphanumeric "key" defined as separate columns with a UNIQUE composite constraint to enforce the business rule.
SQL> create table t1 (id number not null
2 , alpha_bit varchar2(3) not null
3 , numeric_bit number not null
4 , create_date date
5 , constraint t1_pk primary key (id)
6 , constraint t1_uk unique (alpha_bit, numeric_bit)
7 )
8 /
Table created.
SQL>

Resources