2 and only 2 characters for oracle table - oracle

I am trying to create a table in oracle that will accept 2 and only 2 characters. I tried using char(2), but if I put in 1 character in an insert statement, it will accept it. How do I make oracle only accept any inserts of 2 exact characters and reject 1 and 3 and higher characters? I have searched all over the internet and can't seem to find an answer for this.
Thanks!
Christopher

You can create a CHECK constraint that enforces this restriction
SQL> create table foo (
2 col1 varchar2(2) NOT NULL
3 ,check( length(col1) = 2 )
4 );
Table created.
SQL> insert into foo values( 'ab' );
1 row created.
SQL> ed
Wrote file afiedt.buf
1* insert into foo values( 'a' )
SQL> /
insert into foo values( 'a' )
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.SYS_C0022134) violated

You could use a pre-insert trigger to check the length - I can't think of a way to prevent a single character.

Related

adding a sequence to an existing table

i created a table but i forgot to add a sequence to one of the PK, its a sequence on a form page, i just cant find anything about it, is it possible or do i have to do the form all over again.
i tried to replace the PK but it doesnt give me the option to add the sequence when creating a new one.
i searched everywhere and asked the support in chat (didn't really help since its not their job).
all i could find was this and this.
I'd suggest you to skip Apex in this matter and do the following: presume this is your table:
SQL> create table test
2 (id number constraint pk_test primary key,
3 name varchar2(20)
4 );
Table created.
This is the sequence:
SQL> create sequence myseq;
Sequence created.
As you forgot to specify PK source while creating Apex Form page, never mind - let the database handle it. How? Create a BEFORE INSERT trigger:
SQL> create or replace trigger trg_bi_test
2 before insert on test
3 for each row
4 when (new.id is null)
5 begin
6 :new.id := myseq.nextval;
7 end trg_bi_test;
8 /
Trigger created.
Let's test it: I'm inserting only the NAME (which is what your Apex Form will be doing):
SQL> insert into test (name) values ('Littlefoot');
1 row created.
What is table's contents?
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
SQL>
See? Trigger automatically inserted ID (primary key) column value.
If it were an Interactive Grid (which lets you insert several records at a time):
SQL> insert into test (name)
2 select 'Bigfoot' from dual union all
3 select 'FAD' from dual;
2 rows created.
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
2 Bigfoot
3 FAD
SQL>
Works just fine.
And what's another benefit: you don't have to modify Apex application at all.

PLSQL Trigger - Update another table before inserting a new record

I have 3 tables that are related to each other:
ACCOUNTS
CARDS
TRANSACTIONS
I want to change the money amount from account every time I execute a new transaction. I want to decrease the account value with each new move.
I tried writing this trigger:
create or replace trigger ceva_trig1
before insert on miscari
for each row
declare
new_val micari.valoare%tipe := new.valoare;
begin
update conturi
set sold = sold - new_val
where nrcont = (select nrcont
from conturi
join carti_de_credit on conturi.nrcont = carti_de_credit.nrcont
join miscari on carti_de_credit.nr_card = miscari.nrcard)
and sold >= new_val;
end;
May anyone help me correct the syntax that crashes here?
I've created those tables with minimal number of columns, just to make trigger compile.
SQL> create table conturi
2 (sold number,
3 nrcont number
4 );
Table created.
SQL> create table miscari
2 (valoare number,
3 nrcard number
4 );
Table created.
SQL> create table carti_de_credit
2 (nrcont number,
3 nr_card number
4 );
Table created.
Trigger:
SQL> create or replace trigger ceva_trig1
2 before insert on miscari
3 for each row
4 begin
5 update conturi c
6 set c.sold = c.sold - :new.valoare
7 where c.nrcont = (select r.nrcont
8 from carti_de_credit r
9 where r.nrcont = c.nrcont
10 and r.nr_card = :new.nrcard
11 )
12 and c.sold >= :new.valoare;
13 end;
14 /
Trigger created.
SQL>
How does it differ from your code? Like this:
SQL> create or replace trigger ceva_trig1
2 before insert on miscari
3 for each row
4 declare
5 new_val micari.valoare%tipe := new.valoare;
6 begin
7 update conturi
8 set sold = sold - new_val
9 where nrcont = (select nrcont
10 from conturi
11 join carti_de_credit on conturi.nrcont = carti_de_credit.nrcont
12 join miscari on carti_de_credit.nr_card = miscari.nrcard)
13 and sold >= new_val;
14 end;
15 /
Warning: Trigger created with compilation errors.
SQL> show err
Errors for TRIGGER CEVA_TRIG1:
LINE/COL ERROR
-------- -----------------------------------------------------------------
2/11 PL/SQL: Item ignored
2/26 PLS-00208: identifier 'TIPE' is not a legal cursor attribute
4/3 PL/SQL: SQL Statement ignored
10/15 PL/SQL: ORA-00904: "NEW_VAL": invalid identifier
10/15 PLS-00320: the declaration of the type of this expression is incomplete or malformed
SQL>
Explained:
it isn't tipe but type
new column values are referenced with a colon, i.e. :new.valoare
you shouldn't make typos regarding table & column names; it is miscari, not micari
it is bad practice to write query which references the same table (miscari, line #12) trigger is created for. As it is being changed, you can't select values from it as it is mutating
lucky you, you don't have to do that at all. How? Have a look at my code.
Attempting to maintain an ongoing for transactions in one table in another table is always a bad idea. Admittedly in an extremely few cases it's necessary, but should be the design of last resort not an initial one; even when necessary it's still a bad idea and therefore requires much more processing and complexity.
In this instance after you correct all the errors #Littlefoot points out then your real problems begin. What do you do when: (Using Littlefoot's table definitions)
I delete a row from miscari?
I update a row in miscari?
The subselect for nrcont returns 0 rows?
The condition sold >= new_val is False?
If any of conditions occur the value for sold in conturi is incorrect and may not be correctable from values in the source table - miscari. An that list may be just the beginning of the issues you face.
Suggestion: Abandon the idea of keeping an running account of transaction values. Instead derive it when needed. You can create a view that does that and select from the view.
So maybe instead of "create table conturi ..."

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

trigger fails in Oracle

EDIT: Per request here is what's happening in SQL*Plus
EDIT 2: The issue was the column name being a keyword
SQL> drop table comments;
Table Dropped.
SQL> create table comments (
2 comment_id number not null,
3 post_id number not null,
4 user_id number not null,
5 message varchar2(2500) not null,
6 timestamp timestamp(6) not null);
Table Created
SQL> create sequence comment_seq
2 start with 1
3 increment by 1
4 nomaxvalue;
Sequence Created
SQL> ed
Wrote file afiedt.buf //NOTEPAD OPENED UP
1 create or replace trigger comment_trigger
2 before insert on comments
3 for each row
4 begin
5 select comment_seq.nextval
6 into :new.comment_id
7 from dual;
8 end;
SQL> /
ERROR at Line 2:
ORA-06552: PL/SQL: Compilation unit analysis terminated
ORA-06553: PLS-320: the declaration of the type of this expression is
incomplete or malformed
SQL> show errors
No errors.
Assuming I correct the data types of the numeric columns (number(300) is not a valid data type), that code seems to work for me. If you're getting an error, that implies that you are doing something other than what you've posted here.
SQL> create table comments (
2 comment_id number not null,
3 post_id number not null,
4 user_id number not null,
5 message varchar2(2500) not null,
6 timestamp timestamp(6) not null
7 );
Table created.
SQL> create sequence comment_seq
2 start with 1
3 increment by 1
4 nomaxvalue;
Sequence created.
SQL> ed
Wrote file afiedt.buf
1 create or replace trigger comment_trigger
2 before insert on comments
3 for each row
4 begin
5 select comment_seq.nextval
6 into :new.comment_id
7 from dual;
8* end;
SQL> /
Trigger created.
Expanding on comment...
The error is caused by the column named timestamp in the comments table. Using a reserved word as an object name is not a good idea as it can lead to confusion and obscure problems, and can need to be escaped (as here).
In this case, even though the table was created without error, the trigger (though it could be any compiled PL/SQL 'library unit') fails, specifically because the column name is a datatype rather than just a reserved word.
This seems to be bug 6331062, 'PLS-320 if a column name is a datatype (eg "TIMESTAMP")', which the bug report says has been fixed in patchsets 10.2.0.5 (see note 1088172.1) and 11.1.0.7, and in the 11gR2 base release - which would explain why Justin and Craig didn't see it. (I assume Justin is on 11gR2; Craig's output shows he's on 11.1.0.7).
What version of Oracle?
Also, what happens if you just put the trigger code straight into SQL*Plus instead of using "ed"?
But strange, because your script works just fine for me:
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0
SQL> create table comments (
2 comment_id number not null,
3 post_id number not null,
4 user_id number not null,
5 message varchar2(2500) not null,
6 timestamp timestamp(6) not null);
Table created.
SQL> create sequence comment_seq
2 start with 1
3 increment by 1
4 nomaxvalue;
Sequence created.
SQL> ed
Wrote file afiedt.buf
1 create or replace trigger comment_trigger
2 before insert on comments
3 for each row
4 begin
5 select comment_seq.nextval
6 into :new.comment_id
7 from dual;
8* end;
SQL> /
Trigger created.

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