Auto-increment in Oracle table when insert in the same table - sql

I have a table and I want to increment a column by 1 when I insert a row into the same table.
Table users - when I insert first row value of idusers is 1, and in second row value is 2 ....
This is the table
USERS
EMAIL primary key
USERNAME
PASSWORD
IDUSER and this the column I want to be AUTO_INCREMENT
I have tried this code
CREATE SEQUENCE seq_person
MINVALUE 1
START WITH 1
INCREMENT BY 1
CACHE 10;
create or replace trigger incrementIdUser
before insert on users
for each row
begin
select seq_person.nextval into :new.IDUSER from users;
end;
But I get an error when I insert a row:
Erreur lors de l'enregistrement des modifications de la table "SOCIAL"."USERS" :
Ligne 1 : ORA-01403: no data found
ORA-01403: no data found
ORA-06512: at "SOCIAL.INCREMENTIDUSER", line 2
ORA-04088: error during execution of trigger 'SOCIAL.INCREMENTIDUSER'
ORA-06512: at line 1

Not like that, but
create or replace trigger incrementIdUser
before insert on users
for each row
begin
:new.iduser := seq_person.nextval;
end;
Code you wrote selects from users table (which is empty, hence NO_DATA_FOUND). If it contained more than a single row, you'd get TOO_MANY_ROWS (as you're selecting into a scalar variable (:new.iduser). Finally, there's danger of mutating table error as you can't select from a table which is just being modified (in this trigger type).

Insetead of select seq_person.nextval into :new.IDUSER from users; to assign sequence value into iduser you need to use :new.IDUSER :=seq_person.nextval;
create or replace trigger incrementIdUser
before insert on users
for each row
begin
:new.IDUSER :=seq_person.nextval;
end;

You get that error because there are zero rows in the USERS table so SELECT ... FROM USERS returns no rows.
What you want is to either use a table that will always return a single row (which, in Oracle, is the DUAL table):
create or replace trigger incrementIdUser
before insert on users
for each row
begin
select seq_person.nextval into :new.IDUSER from DUAL;
end;
Or, the better solution, is to not use an SQL statement and use pure PL/SQL:
create or replace trigger incrementIdUser
before insert on users
for each row
begin
:new.IDUSER := seq_person.nextval;
end;

Instead of using a trigger, you should use an identity column in the create table statement:
create table users
(iduser integer generated by default on null as identity (nomaxvalue nocache order),
...);

Related

Updating the value of other column while insertion in oracle

My task is pretty much simple let's say I have table having 3 columns i.e. temp(ids, name_c, UID_c) and I have first two column values and 3rd column is nullable. What I want to do is whenever these two value is inserted the value of third column must be updated (after insertion) with new value. i.e concatenation of both values.
For Ex.
insert into temp(ids, name_c) values(did.nextval,'Andrew');
The result should be
1 Andrew Andrew_1
So I am using trigger for this purpose
create or replace trigger triggerDemo
after INSERT
on temp
for each row
declare
/* pragma autonomous_transaction;*/
user_name varchar2(50);
current_val number;
begin
select did.currval into current_val from dual; /* did is sequence */
select names into user_name from temp where ids = current_val;
update temp set uid_c = user_name||'_'||ids where ids = current_val;
end;
When I am inserting the values I get this error
01403. 00000 - "no data found"
*Cause: No data was found from the objects.
*Action: There was no data from the objects which may be due to end of fetch.
First, you want a before insert trigger, not an after insert trigger. Second, decide whether you want to calculate the id on input or in the trigger. You can do something like this:
create or replace trigger triggerDemo before INSERT on temp
for each row
/* pragma autonomous_transaction;*/
begin
if :new.current_val is null then
select did.currval into :new.ids from dual; /* did is sequence */
end;
select :new.user_name || '_' || :new.ids into :new.uid_c from dual;
end;

PLS-00103: Encountered the symbol "¿"

I am trying to make sure that my primary key auto increments. The code below is what I have tried so far.
create or replace trigger field_null
before insert on table
for each row
begin
if :new.number_id is null then
select number_id_SEQ.nextval into :new.number_id from table;
end if;
end;​
Instead of table in select query,try to use dual. Try this General Trigger syntax to create trigger for Auto-increment column
CREATE OR REPLACE TRIGGER %triggername%
BEFORE INSERT ON %tablename% FOR EACH ROW
BEGIN
SELECT %seqname%.NEXTVAL
INTO :NEW.%columnname%
FROM DUAL;
END;
%seqname% will be replaced with the name of the sequence.
%triggername% will be replaced with the name of the trigger.
%columnname% will be replaced with the name of the associated column.
And to Create Sequence, You can use the following syntax:-
CREATE SEQUENCE %seqname%
START WITH 1
INCREMENT BY 1;
Refer Here

Oracle trigger insert other table

I have 2 tables which are my_school and my_class
And "my_school" table has 'info_id' column and also "my_class" table has 'info_id' then I want to get a query that automatically generate "info_id" then I found solution..
Here are my working TRIGGER on "my_school" table...
CREATE OR REPLACE TRIGGER info_id
before insert on my_direction
for each row
begin
if :NEW.WAY_ID is null then
:NEW.WAY_ID := example_id_seq.nextval;
end if;
end;
It works and it's generating auto id when inserting value.
But now how to get this trigger do it on "my_class" table when users insert value on my_school's table then take id with "my_class" table's "info_id" column same time?
You can create trigger on my_school table to update info_id similar to that you have explained and while inserting records, use returning into clause.
Declare a variable to store returned value, for example
v_info_id number(9);
And use it in returning into clause
insert into my_school(column.......list)
values (values........list)
RETURNING info_id INTO v_info_id;
Use v_info_id in your program to insert value of info_id into another tables.

PLPGSQL Cascading Triggers?

I am trying to create a trigger, so that when ever I add a new record it adds another record in the same table. The session field will only take values between 1 and 4. So when I add a 1 in session I want it to add another record but with session 3 blocked. But the problem is that it leads to cascading triggers and it inserts itself again and again because the trigger is triggered when inserted.
I have for example a simple table:
CREATE TABLE example
(
id SERIAL PRIMARY KEY
,name VARCHAR(100) NOT NULL
,session INTEGER
,status VARCHAR(100)
);
My trigger function is:
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO example VALUES (NEW.id + 1, NEW.name, NEW.session+2, 'blocked');
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
Trigger is:
CREATE TRIGGER add_block
AFTER INSERT OR UPDATE
ON example
FOR EACH ROW
EXECUTE PROCEDURE add_block();
I get error:
SQL statement "INSERT INTO example VALUES ( $1 +1, $2 , $3 + 2, $4)"
PL/pgSQL function "add_block" line 37 at SQL statement
This error repeats itself so many times that I can't see the top.
How would I solve this?
EDIT:
CREATE TABLE block_rules
(
id SERIAL PRIMARY KEY
,session INTEGER
,block_session INTEGER
);
This table holds the block rules. So if a new record is inserted into the EXAMPLE table with session 1 then it blocks session 3 accordingly by inserting a new record with blocked status in the same (EXAMPLE) table above (not block_rules). Same for session 2 but it blocks session 4.
The block_rules table holds the rules (or pattern) to block a session by. It holds
id | session | block_session
------------------------------
1 | 1 | 3
2 | 2 | 4
3 | 3 | 2
How would I put that in the WHEN statement of the trigger going with Erwin Branstetter's answer below?
Thanks
New answer to edited question
This trigger function adds blocked sessions according to the information in table block_rules.
I assume that the tables are linked by id - information is missing in the question.
I now assume that the block rules are general rules for all sessions alike and link by session. The trigger is only called for non-blocked sessions and inserts a matching blocked session.
Trigger function:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name
,(SELECT block_session
FROM block_rules
WHERE session = NEW.session)
,'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
Trigger:
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.status IS DISTINCT FROM 'blocked')
EXECUTE PROCEDURE add_block();
Answer to original question
There is still room for improvement. Consider this setup:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name, NEW.session + 2, 'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.session < 3)
-- WHEN (status IS DISTINCT FROM 'blocked') -- alternative guess at filter
EXECUTE PROCEDURE add_block();
Major points:
For PostgreSQL 9.0 or later you can use a WHEN condition in the trigger definition. This would be most efficient. For older versions you use the same condition inside the trigger function.
There is no need to add a column, if you can define criteria to discern auto-inserted rows. You did not tell, so I assume that only auto-inserted rows have session > 2 in my example. I added an alternative WHEN condition for status = 'blocked' as comment.
You should always provide a column list for INSERTs. If you don't, later changes to the table may have unexpected side effects!
Do not insert NEW.id + 1 in the trigger manually. This won't increment the sequence and the next INSERT will fail with a duplicate key violation.
id is a serial column, so don't do anything. The default nextval() from the sequence is inserted automatically.
Your description only mentions INSERT, yet you have a trigger AFTER INSERT OR UPDATE. I cut out the UPDATE part.
The keyword plpgsql doesn't have to be quoted.
OK so can't you just add another column, something like this:
ALTER TABLE example ADD COLUMN trig INTEGER DEFAULT 0;
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
IF NEW.trig = 0 THEN
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked', 1);
END IF;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
it's not great, but it works :-)
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
SET SESSION session_replication_role = replica;
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked');
SET SESSION session_replication_role = origin;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';

Oracle - Insert New Row with Auto Incremental ID

I have a workqueue table that has a workid column. The workID column has values that increment automatically. Is there a way I can run a query in the backend to insert a new row and have the workID column increment automatically?
When I try to insert a null, it throws error ORA01400 - Cannot insert null into workid.
insert into WORKQUEUE (facilitycode,workaction,description) values ('J', 'II', 'TESTVALUES')
What I have tried so far - I tried to look at the table details and didn't see any auto-increment. The table script is as follow
"WORKID" NUMBER NOT NULL ENABLE,
Database: Oracle 10g
Screenshot of some existing data.
ANSWER:
I have to thank each and everyone for the help. Today was a great learning experience and without your support, I couldn't have done. Bottom line is, I was trying to insert a row into a table that already has sequences and triggers. All I had to do was find the right sequence, for my question, and call that sequence into my query.
The links you all provided me helped me look these sequences up and find the one that is for this workid column. Thanks to you all, I gave everyone a thumbs up, I am able to tackle another dragon today and help patient care take a step forward!"
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select max(ID)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES')
It worked for me but would not work with an empty table, I guess.
To get an auto increment number you need to use a sequence in Oracle.
(See here and here).
CREATE SEQUENCE my_seq;
SELECT my_seq.NEXTVAL FROM DUAL; -- to get the next value
-- use in a trigger for your table demo
CREATE OR REPLACE TRIGGER demo_increment
BEFORE INSERT ON demo
FOR EACH ROW
BEGIN
SELECT my_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
/
There is no built-in auto_increment in Oracle.
You need to use sequences and triggers.
Read here how to do it right. (Step-by-step how-to for "Creating auto-increment columns in Oracle")
ELXAN#DB1> create table cedvel(id integer,ad varchar2(15));
Table created.
ELXAN#DB1> alter table cedvel add constraint pk_ad primary key(id);
Table altered.
ELXAN#DB1> create sequence test_seq start with 1 increment by 1;
Sequence created.
ELXAN#DB1> create or replace trigger ad_insert
before insert on cedvel
REFERENCING NEW AS NEW OLD AS OLD
for each row
begin
select test_seq.nextval into :new.id from dual;
end;
/ 2 3 4 5 6 7 8
Trigger created.
ELXAN#DB1> insert into cedvel (ad) values ('nese');
1 row created.
You can use either SEQUENCE or TRIGGER to increment automatically the value of a given column in your database table however the use of TRIGGERS would be more appropriate. See the following documentation of Oracle that contains major clauses used with triggers with suitable examples.
Use the CREATE TRIGGER statement to create and enable a database trigger, which is:
A stored PL/SQL block associated with a table, a schema, or the
database or
An anonymous PL/SQL block or a call to a procedure implemented in
PL/SQL or Java
Oracle Database automatically executes a trigger when specified conditions occur. See.
Following is a simple TRIGGER just as an example for you that inserts the primary key value in a specified table based on the maximum value of that column. You can modify the schema name, table name etc and use it. Just give it a try.
/*Create a database trigger that generates automatically primary key values on the CITY table using the max function.*/
CREATE OR REPLACE TRIGGER PROJECT.PK_MAX_TRIGGER_CITY
BEFORE INSERT ON PROJECT.CITY
FOR EACH ROW
DECLARE
CNT NUMBER;
PKV CITY.CITY_ID%TYPE;
NO NUMBER;
BEGIN
SELECT COUNT(*)INTO CNT FROM CITY;
IF CNT=0 THEN
PKV:='CT0001';
ELSE
SELECT 'CT'||LPAD(MAX(TO_NUMBER(SUBSTR(CITY_ID,3,LENGTH(CITY_ID)))+1),4,'0') INTO PKV
FROM CITY;
END IF;
:NEW.CITY_ID:=PKV;
END;
Would automatically generates values such as CT0001, CT0002, CT0002 and so on and inserts into the given column of the specified table.
SQL trigger for automatic date generation in oracle table:
CREATE OR REPLACE TRIGGER name_of_trigger
BEFORE INSERT
ON table_name
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT sysdate INTO :NEW.column_name FROM dual;
END;
/
the complete know how, i have included a example of the triggers and sequence
create table temasforo(
idtemasforo NUMBER(5) PRIMARY KEY,
autor VARCHAR2(50) NOT NULL,
fecha DATE DEFAULT (sysdate),
asunto LONG );
create sequence temasforo_seq
start with 1
increment by 1
nomaxvalue;
create or replace
trigger temasforo_trigger
before insert on temasforo
referencing OLD as old NEW as new
for each row
begin
:new.idtemasforo:=temasforo_seq.nextval;
end;
reference:
http://thenullpointerexceptionx.blogspot.mx/2013/06/llaves-primarias-auto-incrementales-en.html
For completeness, I'll mention that Oracle 12c does support this feature. Also it's supposedly faster than the triggers approach. For example:
CREATE TABLE foo
(
id NUMBER GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 NOCACHE ORDER ) NOT NULL ,
name VARCHAR2 (50)
)
LOGGING ;
ALTER TABLE foo ADD CONSTRAINT foo_PK PRIMARY KEY ( id ) ;
Best approach: Get the next value from sequence
The nicest approach is getting the NEXTVAL from the SEQUENCE "associated" with the table. Since the sequence is not directly associated to any specific table,
we will need to manually refer the corresponding table from the sequence name convention.
The sequence name used on a table, if follow the sequence naming convention, will mention the table name inside its name. Something likes <table_name>_SEQ. You will immediately recognize it the moment you see it.
First, check within Oracle system if there is any sequence "associated" to the table
SELECT * FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>';
will present something like this
Grab that SEQUENCE_NAME and evaluate the NEXTVAL of it in your INSERT query
INSERT INTO workqueue(id, value) VALUES (workqueue_seq.NEXTVAL, 'A new value...')
Additional tip
In case you're unsure if this sequence is actually associated with the table, just quickly compare the LAST_NUMBER of the sequence (meaning the current value) with the maximum id of
that table. It's expected that the LAST_NUMBER is greater than or equals to the current maximum id value in the table, as long as the gap is not too suspiciously large.
SELECT LAST_NUMBER
FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>' AND SEQUENCE_NAME = 'workqueue_seq';
SELECT MAX(ID)
FROM workqueue;
Reference: Oracle CURRVAL and NEXTVAL
Alternative approach: Get the current max id from the table
The alternative approach is getting the max value from the table, please refer to Zsolt Sky answer in this same question
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select count(1)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES');
Note : here need to use count(1) in place of max(id) column
It perfectly works for an empty table also.