Oracle: constraint preventing insert more than N (variable) rows - oracle

I've got a table defining the max number of objects for each customer.
Table_1
id_table_1 numeric primary key
cd_object varchar2(20)
max_number number
Another table stores the object assigned to each customer
Table_2
id_table_2 numeric primary key
cd_customer varchar2(20)
cd_object varchar2(20)
How can I set up a constraint in table_2 in order to prevent more than "max_number" record for each "customer - object" couple?
For example:
Table_1
cd_object / max_number
xxx / 1
yyy / 2
Table_2
insert "customer_1", "xxx" -> OK!
insert "customer_1", "xxx" -> KO!
insert "customer_1", "yyy" -> OK!
insert "customer_1", "yyy" -> OK!
insert "customer_1", "yyy" -> KO!
Thanks in advance for your replies.

This constraint is more complex than a CHECK constraint can handle. One day we hope Oracle will support SQL ASSERTIONS which are constraints of arbitrary complexity.
Meanwhile this can be done (with caution re performance) using materialized views (MVs) and constraints. I blogged about this may years ago: your requirement is very similar to my example 3 there. Applying to your case it would be something like:
create materialized view table_2_mv
build immediate
refresh complete on commit as
select t2.cd_customer, t2.cd_object, t1.max_number, count(*) cnt
from table_2 t2
join table_1 t1 on t1.cd_object = t2.cd_object
group by t2.cd_customer, t2.cd_object, t1.max_number;
alter table table_2_mv
add constraint table_2_mv_chk
check (cnt <= max_number)
deferrable;
Pure trigger-based solutions tend to fail in the real world as when 2 users similtaneously add a record that just takes the count to the maximum, both succeed and when committed leave the table with more rows than the maximum in it!
However, taking into account your comment that you have 2M rows in table_2, which perhaps makes the MV approach above unusable, there could be another approach that does involve triggers:
Create a table that denormalizes information from table_1 and table_2 like this:
create table denorm as
select t2.cd_customer, t2.cd_object, t1.max_number, count(*) cnt
from table_2 t2
join table_1 t1 on t1.cd_object = t2.cd_object
group by t2.cd_customer, t2.cd_object, t1.max_number;
Use a trigger or triggers on table_1 to ensure denorm.max_number is always correct - e.g when table_1.max_number is updated to a new value, update the corresponding denorm table rows.
Use a trigger or triggers on table_2 to update the denorm.cnt value - e.g. when a row is added, increment denorm.cnt, when a row is deleted, decrement it.
Add a check constraint to denorm
alter table denorm
add constraint denorm_chk
check (cnt <= max_number);
This is essentially the same as the MV solution, but avoids the full refresh by using triggers to maintain the denorm table as you go along. It works in a multi-user system because the updates to the denorm table serialize changes to table_2 so that 2 users cannot modify it simultaneously and break the rules.

You can use the trigger on TABLE_2 as following:
-- creating the tables
SQL> CREATE TABLE TABLE_1 (
2 ID_TABLE_1 NUMBER PRIMARY KEY,
3 CD_OBJECT VARCHAR2(20),
4 MAX_NUMBER NUMBER
5 );
Table created.
SQL> CREATE TABLE TABLE_2 (
2 ID_TABLE_2 NUMBER PRIMARY KEY,
3 CD_CUSTOMER VARCHAR2(20),
4 CD_OBJECT VARCHAR2(20)
5 );
Table created.
-- creating the trigger
SQL> CREATE OR REPLACE TRIGGER TRG_TABLE_2_MAX_OBJECT BEFORE
2 INSERT OR UPDATE ON TABLE_2
3 FOR EACH ROW
4 DECLARE
5 LV_MAX_NUMBER TABLE_1.MAX_NUMBER%TYPE;
6 LV_COUNT NUMBER;
7 BEGIN
8 BEGIN
9 SELECT
10 MAX_NUMBER
11 INTO LV_MAX_NUMBER
12 FROM
13 TABLE_1
14 WHERE
15 CD_OBJECT = :NEW.CD_OBJECT;
16
17 EXCEPTION
18 WHEN OTHERS THEN
19 LV_MAX_NUMBER := -1;
20 END;
21
22 SELECT
23 COUNT(1)
24 INTO LV_COUNT
25 FROM
26 TABLE_2
27 WHERE
28 CD_OBJECT = :NEW.CD_OBJECT;
29
30 IF LV_MAX_NUMBER = LV_COUNT AND LV_MAX_NUMBER >= 0 THEN
31 RAISE_APPLICATION_ERROR(-20000, 'Not allowed - KO');
32 END IF;
33
34 END;
35 /
Trigger created.
-- testing the code
SQL> INSERT INTO TABLE_1 VALUES (1,'xxx',1);
1 row created.
SQL> INSERT INTO TABLE_1 VALUES (2,'yyy',2);
1 row created.
SQL> INSERT INTO TABLE_2 VALUES (1,'customer_1','xxx');
1 row created.
SQL> INSERT INTO TABLE_2 VALUES (2,'customer_1','xxx');
INSERT INTO TABLE_2 VALUES (2,'customer_1','xxx')
*
ERROR at line 1:
ORA-20000: Not allowed - KO
ORA-06512: at "TEJASH.TRG_TABLE_2_MAX_OBJECT", line 28
ORA-04088: error during execution of trigger 'TEJASH.TRG_TABLE_2_MAX_OBJECT'
SQL> INSERT INTO TABLE_2 VALUES (3,'customer_1','yyy');
1 row created.
SQL> INSERT INTO TABLE_2 VALUES (4,'customer_1','yyy');
1 row created.
SQL> INSERT INTO TABLE_2 VALUES (5,'customer_1','yyy');
INSERT INTO TABLE_2 VALUES (5,'customer_1','yyy')
*
ERROR at line 1:
ORA-20000: Not allowed - KO
ORA-06512: at "TEJASH.TRG_TABLE_2_MAX_OBJECT", line 28
ORA-04088: error during execution of trigger 'TEJASH.TRG_TABLE_2_MAX_OBJECT'
SQL>
-- Checking the data in the TABLE_2
SQL> SELECT * FROM TABLE_2;
ID_TABLE_2 CD_CUSTOMER CD_OBJECT
---------- -------------------- --------------------
1 customer_1 xxx
3 customer_1 yyy
4 customer_1 yyy
SQL>
Cheers!!

Related

Creating List partition to an already Existing Table

I am trying to Create a list partition Based on the column "REFRESH_FLAG_Y" which has only Y and N as its Values, Below is the Alter Table used to Create the partition
ALTER TABLE "EDW"."LABOR_SCHEDULE_DAY_F" MODIFY
PARTITION BY LIST ("REFRESH_FLAG")
(PARTITION "REFRESH_FLAG_Y" VALUES ('Y') ,
PARTITION "REFRESH_FLAG_N" VALUES ('N')) ;
COMMIT;
But Whenever I execute the code I get an Error message
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
You did tag the question with Oracle 11g tag; do you really use it?
This is a 12c example; it works if everything is OK:
SQL> create table labor_schedule_day_f as
2 select 1 id, 'Y' refresh_flag from dual union all
3 select 2 id, 'N' refresh_flag from dual;
Table created.
SQL> alter table labor_schedule_Day_f modify
2 partition by list (refresh_flag)
3 (partition refresh_flag_y values ('Y'),
4 partition refresh_flag_n values ('N')
5 );
Table altered.
Error you reported means this:
SQL> drop table labor_schedule_day_f;
Table dropped.
SQL> create table labor_schedule_day_f as
2 select 1 id, 'Y' refresh_flag from dual union all
3 select 2 id, 'N' refresh_flag from dual;
Table created.
Insert a row whose REFRESH_FLAG isn't Y nor N (so it violates the rule you specified):
SQL> insert into labor_schedule_day_f values (3, 'X');
1 row created.
Using the same ALTER TABLE statement as previously:
SQL> alter table labor_schedule_Day_f modify
2 partition by list (refresh_flag)
3 (partition refresh_flag_y values ('Y'),
4 partition refresh_flag_n values ('N')
5 );
alter table labor_schedule_Day_f modify
*
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
SQL>
See? Error you got, which means that
which has only Y and N as its Values
isn't true.
P.S. You'd get the same result even if refresh_flag was NULL for some rows.

Need not to insert duplicate records into the main table instead want to update the existing record based on e_id

CREATE TABLE new_details_staging
(
e_id NUMBER(10),
e_name VARCHAR2(30),
portal_desc VARCHAR2(50),
risk_dec VARCHAR2(50),
CONSTRAINT pk_new_details_staging PRIMARY KEY (e_id)
);
INSERT INTO new_details_staging
VALUES (11, 'A', 'AA', 'High');
INSERT INTO new_details_staging
VALUES (22, 'B', 'BB', 'Low');
CREATE TABLE lookup_ref
(
ref_id NUMBER(10),
ref_typ VARCHAR2(30),
ref_typ_desc VARCHAR2(20),
CONSTRAINT pk_lookup_ref PRIMARY KEY (ref_id)
);
INSERT INTO lookup_ref
VALUES (181, 'portal', 'AA');
INSERT INTO lookup_ref
VALUES (182, 'portal', 'BB');
INSERT INTO lookup_ref
VALUES (183, 'risk', 'High');
INSERT INTO lookup_ref
VALUES (184, 'risk', 'Low');
CREATE TABLE new_details_main
(
e_id NUMBER(10),
e_name VARCHAR2(30),
portal NUMBER(20),
risk NUMBER(20),
CONSTRAINT pk_new_details_main PRIMARY KEY (e_id)
);
COMMIT;
Stored procedure
I want to insert records into the main table from the staging through this stored procedure.
create or replace procedure sp_main(ov_err_msg OUT varchar2)
is
begin
INSERT INTO new_details_main (e_id, e_name, portal, risk)
SELECT n.e_id
, n.e_name
, (
SELECT lp.ref_id
FROM lookup_ref lp -- lp is for Lookup-Portal
WHERE lp.ref_typ = 'portal'
AND lp.ref_typ_desc = n.portal_desc
),
(
SELECT lr.ref_id
FROM lookup_ref lr -- lr is for Lookup-Risk
WHERE lr.ref_typ = 'risk'
AND lr.ref_typ_desc = n.risk_dec
)
FROM new_details_staging n
;
end;
This procedure is giving me the exact results which are required but if data is added into the staging table for the same e_id then it should update the record in the main table and not insert the new record. Example,
If I truncate the staging table and inserted a new row in which risk was Low but this time it got updated to High
INSERT INTO new_details_staging
VALUES (22, 'B', 'BB', 'High');
So, in my main table, there should be 2 rows only but for e_id 22 it should have been updated to 183. How can I achieve this using the stored procedure?
I'd say you need MERGE.
Here's an example.
Procedure (I removed OUT parameter for simplicity; you didn't use it anyway). Also, I rewrote it so that it doesn't use subqueries for portal and risk values, but joined the same (lookup_ref) table twice (maybe you'll need to use outer join; data you have now suggest you don't, but - have that on mind):
SQL> CREATE OR REPLACE PROCEDURE sp_main
2 IS
3 BEGIN
4 MERGE INTO new_details_main m
5 USING (SELECT n.e_id,
6 n.e_name,
7 lp.ref_id portal,
8 lr.ref_id risk
9 FROM new_details_staging n
10 JOIN lookup_ref lp
11 ON lp.ref_typ_desc = n.portal_desc
12 AND lp.ref_typ = 'portal'
13 JOIN lookup_ref lr
14 ON lr.ref_typ_desc = n.risk_dec
15 AND lr.ref_typ = 'risk') x
16 ON (m.e_id = x.e_id)
17 WHEN MATCHED
18 THEN
19 UPDATE SET m.e_name = x.e_name, m.portal = x.portal, m.risk = x.risk
20 WHEN NOT MATCHED
21 THEN
22 INSERT (e_id,
23 e_name,
24 portal,
25 risk)
26 VALUES (x.e_id,
27 x.e_name,
28 x.portal,
29 x.risk);
30 END;
31 /
Procedure created.
Testing: run the procedure with "initial" set of data (the one you posted):
SQL> EXEC sp_main;
PL/SQL procedure successfully completed.
Result is then:
SQL> SELECT * FROM new_details_main;
E_ID E_NAME PORTAL RISK
---------- ---------- ---------- ----------
11 A 181 183
22 B 182 184
You said you'll then remove rows from the staging table and insert a new row:
SQL> DELETE FROM new_details_staging;
2 rows deleted.
SQL> INSERT INTO new_details_staging
2 VALUES (22, 'B', 'BB', 'High');
1 row created.
Let's run the procedure again an check new result:
SQL> EXEC sp_main;
PL/SQL procedure successfully completed.
SQL> SELECT * FROM new_details_main;
E_ID E_NAME PORTAL RISK
---------- ---------- ---------- ----------
11 A 181 183
22 B 182 183 --> RISK is modified to 183
SQL>

How to create a unique id for an existing table in PL SQL?

The situation is that, when I import a file into the database, one of the first thing I usually do is to assign an unique ID for each record.
I normally do below in TSQL
ALTER TABLE MyTable
ADD ID INT IDENTITY(1,1)
I am wondering if there is something similar in PL SQL?
All my search result come back with multiple steps.
Then I'd like to know what PL SQL programmer typically do to ID records after importing a file. Do they do that?
The main purpose for me to ID these records is to trace it back after manipulation/copying.
Again, I understand there is solution there, my further question is whether PL SQL programmer actually do that, or there is other alternative which making this step not necessary in PL SQL?
OK then, as you're on Oracle 11g, there's no identity column there so - back to multiple steps. Here's an example:
I'm creating a table that simulates your imported table:
SQL> create table tab_import as
2 select ename, job, sal
3 from emp
4 where deptno = 10;
Table created.
Add the ID column:
SQL> alter table tab_import add id number;
Table altered.
Create a sequence which will be used to populate the ID column:
SQL> create sequence seq_imp;
Sequence created.
Update current rows:
SQL> update tab_import set
2 id = seq_imp.nextval;
3 rows updated.
Create a trigger which will take care about future inserts (if any):
SQL> create or replace trigger trg_bi_imp
2 before insert on tab_import
3 for each row
4 begin
5 :new.id := seq_imp.nextval;
6 end;
7 /
Trigger created.
Check what's in the table at the moment:
SQL> select * from tab_import;
ENAME JOB SAL ID
---------- --------- ---------- ----------
CLARK MANAGER 2450 1
KING PRESIDENT 5000 2
MILLER CLERK 1300 3
Let's import some more rows:
SQL> insert into tab_import (ename, job, sal)
2 select ename, job, sal
3 from emp
4 where deptno = 20;
3 rows created.
The trigger had silently populated the ID column:
SQL> select * From tab_import;
ENAME JOB SAL ID
---------- --------- ---------- ----------
CLARK MANAGER 2450 1
KING PRESIDENT 5000 2
MILLER CLERK 1300 3
SMITH CLERK 800 4
JONES MANAGER 2975 5
FORD ANALYST 3000 6
6 rows selected.
SQL>
Shortly: you need to
alter table and add the ID column
create a sequence
create a trigger
The end.
The answer given by #Littlefoot would be my recommendation too - but still I thought I could mention the following variant which will work only if you do not intend to add more rows to the table later.
ALTER TABLE MyTable add id number(38,0);
update MyTable set id = rownum;
commit;
My test:
SQL> create table tst as select * from all_tables;
Table created.
SQL> alter table tst add id number(38,0);
Table altered.
SQL> update tst set id = rownum;
3815 rows updated.
SQL> alter table tst add constraint tstPk primary key (id);
Table altered.
SQL>
SQL> select id from tst where id < 15;
ID
----------
1
2
3
4
5
6
7
8
9
10
11
ID
----------
12
13
14
14 rows selected.
But as mentioned initially,- this only fixes numbering for the rows you have at the time of the update - your'e not going to get new id values for new rows anytime later - if you need that, go for the sequence solution.
You can add an id column to a table with a single statement (Oracle 11g, see dbfiddle):
alter table test_
add id raw( 16 ) default sys_guid() ;
Example:
-- create a table without an id column
create table test_ ( str )
as
select dbms_random.string( 'x', 16 )
from dual
connect by level <= 10 ;
select * from test_ ;
STR
ULWL9EXFG6CIO72Z
QOM0W1R9IJ2ZD3DW
YQWAP4HZNQ57C2UH
EETF2AXD4ZKNIBBF
W9SECJYDER793MQW
alter table test_
add id raw( 16 ) default sys_guid() ;
select * from test_ ;
STR ID
ULWL9EXFG6CIO72Z 0x782C6EBCAE2D7B9FE050A00A02005D65
QOM0W1R9IJ2ZD3DW 0x782C6EBCAE2E7B9FE050A00A02005D65
YQWAP4HZNQ57C2UH 0x782C6EBCAE2F7B9FE050A00A02005D65
EETF2AXD4ZKNIBBF 0x782C6EBCAE307B9FE050A00A02005D65
W9SECJYDER793MQW 0x782C6EBCAE317B9FE050A00A02005D65
Testing
-- Are the id values unique and not null? Yes.
alter table test_
add constraint pkey_test_ primary key ( id ) ;
-- When we insert more rows, will the id be generated? Yes.
begin
for i in 1 .. 100
loop
insert into test_ (str) values ( 'str' || to_char( i ) ) ;
end loop ;
end ;
/
select * from test_ order by id desc ;
-- last 10 rows of the result
STR ID
str100 0x782C806E16A5E998E050A00A02005D81
str99 0x782C806E16A4E998E050A00A02005D81
str98 0x782C806E16A3E998E050A00A02005D81
str97 0x782C806E16A2E998E050A00A02005D81
str96 0x782C806E16A1E998E050A00A02005D81
str95 0x782C806E16A0E998E050A00A02005D81
str94 0x782C806E169FE998E050A00A02005D81
str93 0x782C806E169EE998E050A00A02005D81
str92 0x782C806E169DE998E050A00A02005D81
str91 0x782C806E169CE998E050A00A02005D81
Regarding your other questions:
{1} Then I'd like to know what PL SQL programmer typically do to ID records after importing a file. Do they do that? The main purpose for me to ID these records is to trace it back after manipulation/copying.
-> As you know, the purpose of an id is: to identify a row. We don't "do anything to IDs". Thus, your usage of IDs seems legit.
{2} Again, I understand there is solution there, my further question is whether PL SQL programmer actually do that, or there is other alternative which making this step not necessary in PL SQL?
-> Not quite sure what you are asking here. Although there is a ROWID() pseudocolumn (see documentation), we should not use it to identify rows.
"You should not use ROWID as the primary key of a table. If you delete
and reinsert a row with the Import and Export utilities, for example,
then its rowid may change. If you delete a row, then Oracle may
reassign its rowid to a new row inserted later."

Oracle: how to drop a subpartition of a specific partition

I am using an oracle 11 table with interval partitioning and list subpartitioning like this (simplified):
CREATE TABLE LOG
(
ID NUMBER(15, 0) NOT NULL PRIMARY KEY
, MSG_TIME DATE NOT NULL
, MSG_NR VARCHAR2(16 BYTE)
) PARTITION BY RANGE (MSG_TIME) INTERVAL (NUMTOYMINTERVAL (1,'MONTH'))
SUBPARTITION BY LIST (MSG_NR)
SUBPARTITION TEMPLATE (
SUBPARTITION login VALUES ('FOO')
, SUBPARTITION others VALUES (DEFAULT)
)
(PARTITION oldvalues VALUES LESS THAN (TO_DATE('01-01-2010','DD-MM-YYYY')));
How do I drop a specific subpartitition for a specific month without knowing the (system generated) name of the subpartition? There is a syntax "alter table ... drop subpartition for (subpartition_key_value , ...)" but I don't see a way to specify the month for which I am deleting the subpartition. The partition administration guide does not give any examples, either. 8-}
You can use the metadata tables to get the specific subpartition name:
SQL> insert into log values (1, sysdate, 'FOO');
1 row(s) inserted.
SQL> SELECT p.partition_name, s.subpartition_name, p.high_value, s.high_value
2 FROM user_tab_partitions p
3 JOIN
4 user_tab_subpartitions s
5 ON s.table_name = p.table_name
6 AND s.partition_name = p.partition_name
7 AND p.table_name = 'LOG';
PARTITION_NAME SUBPARTITION_NAME HIGH_VALUE HIGH_VALUE
--------------- ------------------ ------------ ----------
OLDVALUES OLDVALUES_OTHERS 2010-01-01 DEFAULT
OLDVALUES OLDVALUES_LOGIN 2010-01-01 'FOO'
SYS_P469754 SYS_SUBP469753 2012-10-01 DEFAULT
SYS_P469754 SYS_SUBP469752 2012-10-01 'FOO'
SQL> alter table log drop subpartition SYS_SUBP469752;
Table altered.
If you want to drop a partition dynamically, it can be tricky to find it with the ALL_TAB_SUBPARTITIONS view because the HIGH_VALUE column may not be simple to query. In that case you could use DBMS_ROWID to find the subpartition object_id of a given row:
SQL> insert into log values (4, sysdate, 'FOO');
1 row(s) inserted.
SQL> DECLARE
2 l_rowid_in ROWID;
3 l_rowid_type NUMBER;
4 l_object_number NUMBER;
5 l_relative_fno NUMBER;
6 l_block_number NUMBER;
7 l_row_number NUMBER;
8 BEGIN
9 SELECT rowid INTO l_rowid_in FROM log WHERE id = 4;
10 dbms_rowid.rowid_info(rowid_in =>l_rowid_in ,
11 rowid_type =>l_rowid_type ,
12 object_number =>l_object_number,
13 relative_fno =>l_relative_fno ,
14 block_number =>l_block_number ,
15 row_number =>l_row_number );
16 dbms_output.put_line('object_number ='||l_object_number);
17 END;
18 /
object_number =15838049
SQL> select object_name, subobject_name, object_type
2 from all_objects where object_id = '15838049';
OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE
--------------- --------------- ------------------
LOG SYS_SUBP469757 TABLE SUBPARTITION
As it turns out, the "subpartition for" syntax does indeed work, though that seems to be a secret Oracle does not want to tell you about. :-)
ALTER TABLE TB_LOG_MESSAGE DROP SUBPARTITION FOR
(TO_DATE('01.02.2010','DD.MM.YYYY'), 'FOO')
This deletes the subpartition that would contain MSG_TIME 2010/02/01 and MSG_NR FOO. (It is not necessary that there is an actual row with this exact MSG_TIME and MSG_NR. It throws an error if there is no such subpartition, though.)
Thanks for the post - it was very useful for me.
One observation though on the above script to identify the partition and delete it:
The object_id returned by dbms_rowid.rowid_info is not the object_id of the all_objects table. It is actually the data_object_id. It is observed that usually these ids match. However, after truncating the partitioned table several times, these ids diverged in my database. Hence it might be reasonable to instead use the data_object_id to find out the name of the partition:
select object_name, subobject_name, object_type
from all_objects where data_object_id = '15838049';
From the table description of ALL_OBJECTS:
OBJECT_ID Object number of the object
DATA_OBJECT_ID Object number of the segment which contains the object
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_rowid.htm
In the sample code provided in the above link, DBMS_ROWID.ROWID_OBJECT(row_id) is used instead to derive the same information that is given by dbms_rowid.rowid_info. However, the documentation around this sample mentions that it is a data object number from the ROWID.
Examples
This example returns the ROWID for a row in the EMP table, extracts
the data object number from the ROWID, using the ROWID_OBJECT function
in the DBMS_ROWID package, then displays the object number:
DECLARE object_no INTEGER; row_id ROWID; ... BEGIN
SELECT ROWID INTO row_id FROM emp
WHERE empno = 7499; object_no := DBMS_ROWID.ROWID_OBJECT(row_id); DBMS_OUTPUT.PUT_LINE('The obj. # is
'|| object_no); ...

Auto Increment for Oracle

I need to create a sequence and a trigger to auto-increment the primary key on a table but I have no idea on how to do it.
Create the table and the sequence
SQL> create table staff (
2 emp_id number primary key,
3 staff_name varchar2(100)
4 );
Table created.
SQL> create sequence emp_id_seq;
Sequence created.
Now, you can create a trigger that uses the sequence to populate the primary key
SQL> create trigger trg_emp_id
2 before insert on staff
3 for each row
4 begin
5 select emp_id_seq.nextval
6 into :new.emp_id
7 from dual;
8 end;
9 /
Trigger created.
Now, when you insert data, you woon't need to specify the EMP_ID column-- it will automatically be populated by the trigger
SQL> insert into staff( staff_name ) values ('Justin');
1 row created.
SQL> select * from staff;
EMP_ID STAFF_NAME
---------- --------------------
1 Justin
Read this, Beautiful article.
how sequence [auto increment in oracle]
syntax
Create sequence sequence_name
start with value
increment by value
minvalue value
maxvalue value;
example
SQL> create table emp (
emp_id number(10),
fname varchar2(25),
lname varchar2(25),
constraint pk_emp_id PRIMARY KEY(emp_id)
);
SQL> Create sequence emp_sequence
start with 1
increment by 1
minvalue 1
maxvalue 10000;
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Darvin','Johnson');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Mig','Andrews');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Alex','Martin');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Jon','paul');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Yatin','Bones');
in emp_sequence.nextval where emp_sequence is the name of sequence we created above and nextval is a function that is used to assign the next number from emp_sequence to emp_id column in emp table.
SQL> select * from emp;
EMP_ID FNAME LNAME
---------- ------------------------- -------------------------
1 Darvin Johnson
2 Mig Andrews
3 Alex Martin
4 Jon paul
5 Yatin Bones
Try this:
create sequence seq_EmpID start with 1 increment by 1
insert into Emp_Table values(seq_EmpID.nextval,'Ram')
I am not sure which version of Oracle you are using, but the following will work in 19c:
create table staff (
emp_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
staff_name varchar2(100)
);
https://docs.oracle.com/en/database/other-databases/nosql-database/19.3/java-driver-table/creating-tables-identity-column.html
The command above creates a system sequence that is automatically employed to populate the key value. you cannot drop the sequence created as it is a system sequence, but because of the dependency on the column when the table is dropped and the recycle bin purged it is removed.
Very good question!!
Probably sequence can be used in this way - also, I am not sure if there really is a difference :
CREATE SEQUENCE emp_id_seq MINVALUE 1 START WITH 1 INCREMENT BY 1 CACHE 10;
First creating the table :
sql-> create table item(id int primary key, name varchar(25),qty int, price int);
Now we want to make auto increment sequence to the first column i.e. id
sql-> CREATE SEQUENCE id MINVALUE 1 START WITH 1 CACHE 10; //system saves the last 10 items in temp memory
This will create auto increment.
Now we are inserting data:
sql-> insert into item VALUES(id.nextval,'ddcd',2,4);
sql-> insert into item VALUES(id.nextval,'ddcd',676,4);
Finally Displaying the table :
SQL> select * from item;
ID NAME QTY PRICE
1 ddcd 2 4
2 ddcd 676 4
If you use a sequence for several tables, because the value of the sequence is inconsistent example:
we have two tables emp and depeartement:
If I use the sequence on emp I would have: ID_dept = 6 because the 5 is already used in the other table.
example :
SQL> insert into emp values(masequence.nextval,'aaa');
1 ligne crÚÚe.
SQL> insert into departement values(masequence.nextval,'aaa');
1 ligne crÚÚe.
SQL> select * from emp;
ID_EMP NOM_EMP
---------- -------------------------
5 aaa
SQL> select * from departement;
ID_DEPT NOM_DEPT
---------- ----------
6 aaa
SQL>

Resources