how auto generate number start from specific number the first 4 # include the current year in oracle 11g - sql

i have table which have id when iwnna to insert new record its by default generate new # increment by 1 how can i do it please
the number is begin with year and serial for ex; 20130001,20130002,20130003 and so on , when the year is end then will start 20140001,20140002,20140003

Putting aside the question why you would want to do this.
The most straightforward approach is to create an Oracle SEQUENCE object, starting at the value you want to start with, increment of 1. As an example:
CREATE SEQUENCE myseq START WITH 20130001 INCREMENT BY 1 NOCACHE ;
To make use of the sequence object to supply a value for a table on an INSERT, create a BEFORE INSERT trigger
CREATE TRIGGER mytable_trg_bi
BEFORE INSERT ON mytable
FOR EACH ROW
BEGIN
IF NEW.mycol IS NULL THEN
SELECT myseq.NEXTVAL FROM DUAL INTO NEW.mycol;
END IF;
END;
/
(It's been a while since I've worked with the Oracle syntax; some of the keywords might not be in the right order. But this is the normal pattern for assigning unique, system generated values to a column on an INSERT.
That part is easy.
The trickier part is getting the sequence to "jump" to a specific value.
There's a couple of approaches to doing that. One would be drop the sequence object and re-create a new object, with the same name, with a new starting value. But that's not very elegant, and fairly disruptive.
Another approach is to modify the increment value, select nextval to make it jump, and then set the increment back to 1.
As a rough outline of what that might look like:
DECLARE
ln_val NUMBER;
BEGIN
-- retrieve next value from sequence
SELECT myseq.NEXTVAL FROM DUAL INTO ln_val;
-- set increment so next call to nextval will "jump"
EXECUTE IMMEDIATE
'ALTER SEQUENCE myseq INCREMENT BY '|| 20140001 - 2 - ln_val ||' NOCACHE';
-- this should return us 20140000
SELECT myseq.NEXTVAL FROM DUAL INTO ln_val;
-- reset increment back to 1
EXECUTE IMMEDIATE
'ALTER SEQUENCE myseq INCREMENT BY 1';
END;
/
Note that this approach of setting/resetting the current value of the sequence is subject to a race condition, if another session is pulling values from the SEQUENCE at the same time.

Related

how to define a trigger that gives id automatically when insert a new person?

as I described on the title, I want to write a trigger that defines to add a new staff by all giving attributes except ID, I want to trigger generate and insert it automatically. How can I do that?
I've written a code like below in PL/SQL, but it's including the sequence and I couldn't find how can I get the current max ID of my staff with using the sequence, so could you please help me, with or without using the sequence?
CREATE SEQUENCE BEFORE_INSERTING START WITH 1000 INCREMENT BY 1;
CREATE OR REPLACE TRIGGER NEW_ID_BEFORE_INSERTING
BEFORE INSERT ON STAFF
FOR EACH ROW
BEGIN
:NEW.STAFF_ID := BEFORE_INSERTING.nextval;
END;
/
By the way, this code works fine but as you see it's starting from 1000.
Perhaps you can use something like the following to find the maximum value for STAFF_ID and then redefine the sequence based on that value:
DECLARE
nMax_staff_id NUMBER;
BEGIN
SELECT MAX(STAFF_ID)
INTO nMax_staff_id
FROM STAFF;
EXECUTE IMMEDIATE 'DROP SEQUENCE BEFORE_INSERTING';
EXECUTE IMMEDIATE 'CREATE SEQUENCE BEFORE_INSERTING START WITH ' ||
nMax_staff_id + 1 || ' INCREMENT BY 1';
END;
You only need to run the above once, just to get the sequence reset. After that the trigger will use the sequence to obtain each STAFF_ID value. Note that there are other ways to redefine a sequence's value, but here we'll do The Simplest Thing That Could Possibly Work, which is to drop the sequence and then recreate it with the new starting value.
Best of luck.
in order to find the max STAFF_ID you need the below select:
select max(STAFF_ID) from STAFF;
Once you have the highest STAFF_ID you can re-create the sequence as desired.
In any case you can increment a sequence like so:
ALTER SEQUENCE seq_name INCREMENT BY 1;
I hope that helps!
Please don't hesitate to leave a comment for any further clarifications.
Ted.
Using the sequence guarantees uniqueness of STAFF_ID but does not guarantee no gaps in assigning STAFF_ID. You might end up with STAFF_ID like 100, 101, 103, 106..
First, get the max(STAFF_ID) while the system is not running. Something like
select max(staff_id) from staff;
Then, create the sequence to start from the max staff_id. Something like
create sequence staff_sequence start with <max_id> + 1 increment by 1 nocache;
"NOCACHE" minimizes the chance of having gaps in the staff_id assigned
After, use the trigger that you created to get the nextval from the seuqnece
Note the following:
- Once a sequence is invoked for nextval, that number dispatched cannot be returned to the sequnece
- Any cached sequence values will be lost if oracle database was shutdown
If your requirement is not to have gaps between staff_ids, then sequence might not be used.

SQL trigger not compiling

iam using Oracle sqldeveloper 3.2.20 and oracleXE112 as my DB. Iam trying to create a trigger, but keep getting this error
Error(2,13): PLS-00357: Table,View Or Sequence reference 'SPRAVCA.REGION_ID_REGION' not allowed in this context
I have table SPRAVCA with foreign key REGION_ID_REGION that is linked to primary key ID_REGION in table REGION. I have attribute pocet_sp that represents number of SPRAVCA entities that are assigned to specific REGION entity.
My script lookes like this
CREATE OR REPLACE TRIGGER calc_poc_sp
AFTER INSERT OR UPDATE
ON SPRAVCA
FOR EACH ROW
BEGIN
IF (SPRAVCA.REGION_ID_REGION) = (REGION.ID_REGION) THEN
NEW.POCET_SP := OLD.POCET_SP + 1;
END IF;
END calc_poc_sp;
I want to create a trigger, that will automaticly increment pocet_sp in REGION entity if any SPRAVCA with matching foregin key will be created. Thanks a lot in advance !
In oracle:
IF (SPRAVCA.REGION_ID_REGION) = (REGION.ID_REGION) THEN
doesnt work. This is not like java or any other programming language. here the sequence object is an object which has 2 methods that can access the value. CURRVAL and NEXT VAL.
NEXTVAL will increment your current value and retrieve the incremented value. but CURRVAL will retireve value that is currently.
Unfortunately. i have experienced that the CURRVAL generally doesnt work untill the NEXTVAL hasnt been called on the sequence in the same Connection session.
Please specify use case as in why u need to check the current value of the Sequence.
Still a corrected version will be:
CREATE OR REPLACE TRIGGER calc_poc_sp
AFTER INSERT OR UPDATE
ON SPRAVCA
FOR EACH ROW
DECLARE
VAL_CHECK REGION.ID_REGION%TYPE;
CURSOR REGION IS SELECT * FROM REGION;
BEGIN
--
-- SELECT SPRAVCA.REGION_ID_REGION.CURRVAL INTO VAL_CHECK FROM DUAL;
SELECT REGION_ID_REGION INTO VAL_CHECK FROM SPRAVCA <ADD YOUR WHERE CLAUSE SO THAT ONLY 1 ROW IS RETURNED>;
--I have added a loop statement so that u can iterate through all the values of the region table. If you dont want to loop then add a where clause where you see the REGION cursor being defined.
FOR 1 IN REGION LOOP
IF (VAL_CHECK) = (REGION.ID_REGION) THEN
--
NEW.POCET_SP := OLD.POCET_SP + 1;
--
END IF;
END LOOP;
END calc_poc_sp;
Please see the syntax as i do not have a DB access as of now.
Also a question:
Where have you defined what is REGION in your trigger. This will be an error as you have not defined it.

Same seq_name.nextval in one query. ORACLE

how can you select the same sequence twice in one query?
I have googled this and just cant get an answer.
Just to be clear, this is what I need, example :
select seq.nextval as v1, seq.nextval as v2 from dual (I know that doesn't work)
I have tried UNION as well, Just cant get my head around it.
If you always need exactly two values, you could change the sequence to increment by 2:
alter sequence seq increment by 2;
and then select the current value and its successor:
select seq.nextval,
seq.nextval + 1
from dual;
Not pretty, but it should do the job.
UPDATE
Just to clarify: the ALTER SEQUENCE should be issued only once in a lifetime, not once per session!
The Answer by Frank has a downside:
You cannot use it in transactional system, because the ALTER SEQUENCE commits any pending DML.
The Answer of Sean only pulls the sequence once.
As I understand, you want to pull two values.
As an alternative to Seans solution, you could also select two times from .nextval, due ORACLE gives you the same value twice.
I'd prefer wrapping up the sequence in a procedure.
This tricks oracle to pulling the sequence twice.
CREATE or replace FUNCTION GetSeq return number as
nSeq NUMBER;
begin
select seq.nextval into nSeq from dual;
return nSeq;
end;
/
If you need this generically, maybe you'd like:
CREATE or replace FUNCTION GetSeq(spSeq in VARCHAR2) return number as
nSeq NUMBER;
v_sql long;
begin
v_sql:='select '||upper(spSeq)||'.nextval from dual';
execute immediate v_sql into nSeq;
return nSeq;
end;
/
There are some limitations on how NEXTVAL can be used. There's a list in the Oracle docs. More to the point, the link includes a list of conditions where NEXTVAL will be called more than once.
The only scenario named on the page where a straight SELECT will call NEXTVAL more than once is in "A top-level SELECT statement". A crude query that will call NEXTVAL twice is:
SELECT seq.NEXTVAL FROM (
SELECT * FROM DUAL
CONNECT BY LEVEL <= 2) -- Change the "2" to get more sequences
Unfortunately, if you try to push this into a subquery to flatten out the values to one row with columns v1 and v2 (like in your question), you'll get the ORA-02287: sequence number not allowed here.
This is as close as I could get. If the query above won't help you get where you want, then check out the other answers that have been posted here. As I've been typing this, a couple of excellent answers have been posted.
Telling you to just call sequence.nextval multiple times would be boring, so here's what you can try:
create type t_num_tab as table of number;
CREATE SEQUENCE SEQ_TEST
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
CACHE 100
NOORDER;
create or replace function get_seq_vals(i_num in number) return t_num_tab is
seq_tab t_num_tab;
begin
select SEQ_TEST.nextval
bulk collect into seq_tab
from all_objects
where rownum <= i_num;
return seq_tab;
end;
And you can use it as follows. This example is pulling 7 numbers at once from the sequence:
declare
numbers t_num_tab;
idx number;
begin
-- this grabs x numbers at a time
numbers := get_seq_vals(7);
-- this just loops through the returned numbers
-- to show the values
idx := numbers.first;
loop
dbms_output.put_line(numbers(idx));
idx := numbers.next(idx);
exit when idx is null;
end loop;
end;
Also note that I use "next" instead of first..last as its possible you may want to remove numbers from the list before iterating through (or, sometimes a sequence cache can results in numbers incrementing by more than 1).
These are all great answers, unfortunately it was just not enough. Will definitely be able to return to this page when struggling in the future.
I have gone with a different method. Asked a new question and got my answer.
You can go check it out here.

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.

How do I automatically reset a sequence's value to 0 every year in Oracle 10g?

As in the question, how do I automatically reset an Oracle sequence's value back to 0 every year in Oracle 10g?
I'm using the sequence to generate an identifier in the format YYYY<sequence value> and the sequence value has to be reset to 0 every year.
YYYY is obtained from java and concatenated with the sequence value from Oracle. The format of the identifier can't be changed due to external 3rd party requirements. Thanks for any help in advance.
Sequences aren't really designed to be reset. But there are some cases where resetting a sequence is desirable, for example, when setting up test data, or merging production data back into a test environment. This type of activity is not normally done in production.
IF this type of operation is going to be put into production, it needs to thoroughly tested. (What causes the most concern is the potential for the reset procedure to be accidentally performed at the wrong time, like, in the middle of the year.
Dropping and recreating the sequence is one approach. As an operation, it's fairly straightforward as far as the SEQUENCE goes:
DROP SEQUENCE MY_SEQ;
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 0;
[EDIT] As Matthew Watson correctly points out, every DDL statement (such as a DROP, CREATE, ALTER) will cause an implicit commit. [/EDIT]
But, any privileges granted on the SEQUENCE will be dropped, so those will need to be re-granted. Any objects that reference the sequence will be invalidated. To get this more generalized, you would need to save privileges (before dropping the sequence) and then re-grant them.
A second approach is to ALTER an existing SEQUENCE, without dropping and recreating it. Resetting the sequence can be accomplished by changing the INCREMENT value to a negative value (the difference between the current value and 0), and then do exactly one .NEXTVAL to set the current value to 0, and then change the INCREMENT back to 1. I've used a this same approach before (manually, in a test environment), to set a sequence to a larger value as well.
Of course, for this to work correctly, you need to insure no other sessions reference the sequence while this operation is being performed. An extra .NEXTVAL at the wrong instant will screw up the reset. (NOTE: achieving that on the database side is going to be difficult, if the application is connecting as the owner of the sequence, rather than as a separate user.)
To have it happen every year, you'd need to schedule a job. The sequence reset will have to be coordinated with the reset of the YYYY portion of your identifier.
Here's an example:
http://www.jaredstill.com/content/reset-sequence.html
[EDIT]
UNTESTED placeholder for one possible design of a PL/SQL block to reset sequence
declare
pragma autonomous_transaction;
ln_increment number;
ln_curr_val number;
ln_reset_increment number;
ln_reset_val number;
begin
-- save the current INCREMENT value for the sequence
select increment_by
into ln_increment
from user_sequences
where sequence_name = 'MY_SEQ';
-- determine the increment value required to reset the sequence
-- from the next fetched value to 0
select -1 - MY_SEQ.nextval into ln_reset_increment from dual;
-- fetch the next value (to make it the current value)
select MY_SEQ.nextval into ln_curr from dual;
-- change the increment value of the sequence to
EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by '
|| ln_reset_increment ||' minvalue 0';
-- advance the sequence to set it to 0
select MY_SEQ.nextval into ln_reset_val from dual;
-- set increment back to the previous(ly saved) value
EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by '
|| ln_increment ;
end;
/
NOTES:
how to best protect the sequence from access while it's being reset, RENAME it?
Several test cases to work through here.
First pass, check normative cases of positive, ascending, increment 1 sequence.
would a better approach be to create new SEQUENCE, add permissions, rename existing and new sequences, and then re-compile dependencies?
Just throwing this out there as an idea:
If you want a solution that requires no ongoing DDL (i.e. no dropping and creating or resetting sequences), or even any jobs, you could consider something like this (this is in principle only, I haven't tested this approach but I'm sure it'll work):
Create a single sequence.
Create a reference table, with one row for each year, e.g.
YEARS (year NUMBER(4,0) PRIMARY KEY, starting_value NUMBER)
When you get NEXTVAL from the sequence, you then have to subtract the starting_value when queried from the YEARS table for the current year. If the year is not found, a new row should be inserted (i.e. the first process run in any given year will insert the new value).
e.g. a function, e.g. get_year_starting_value (pn_year IN NUMBER) RETURN NUMBER could query this table and return the starting_value for the given year; if it gets NO_DATA_FOUND, it could call a procedure to insert it using the NEXTVAL from the sequence (committed in an autonomous transaction so that the new value is immediately available to other sessions, and so that the function doesn't fail due to the side effect)
Probably not a solution for all cases, but I think this approach may help in at least some scenarios.
Use a job to do the trick. First, create a stored procedure to reset your sequence (I usually go with the DROP/CREATE solution, but you could use spencer7593's trick) :
CREATE OR REPLACE PROCEDURE my_seq_reset AS
BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE my_seq';
EXECUTE IMMEDIATE
'CREATE SEQUENCE my_seq' ||
' MINVALUE 1 ' ||
' MAXVALUE 999999 ' ||
' START WITH 1 ' ||
' INCREMENT BY 1 ' ||
' NOCACHE';
END;
Then create the job (see here for the reference) :
BEGIN
dbms_scheduler.create_job(
job_name => 'job$my_seq_reset',
job_type => 'STORED_PROCEDURE',
job_action => 'my_seq_reset',
start_date => TO_DATE('01-01-09', 'DD-MM-RR'),
repeat_interval => 'FREQ=YEARLY;BYDATE=0101',
enabled => TRUE,
auto_drop => FALSE,
comments => 'My sequence yearly reset job.'
);
END;
You're done.
I'm not sure there is a good way to do it, this isn't really what sequences are designed for. they are just purely incrementing unique numbers.
2 thoughts come to mind.
At 12am on the first, reset the sequence, this is hard, because you need to make sure you beat any code.
Create a sequence for each year, perhaps even have it in your code to be able to create the sequence, then dynamically call the correct sequence for the year.
I'd tend to favor option 2, as its not trying to do anything fancy and is always going to work without fail, any options trying to manipulate the sequence itself are bound to bite you.
First of all, it doesn't seem to be a way to make the sequence restart automatically every year. Read this for reference:
http://www.psoug.org/reference/OLD/sequences.html?PHPSESSID=5949da378678fa6d24b6fcc6eaae9888
My approach will be:
create a table with the year and the starting sequence for that year (lets call this table year_seed)
create a procedure that receives the year, checks the year_seed table and if it's the first check for the year generates the register with the starting sequence. This procedure must also return the sequence minus the starting sequence for the year.
Maybe it's not so simple but I think it's the best solution. Good luck
I have found that was best to create a trigger and a table.
The table will contain the year and sequence for the year. The trigger gets the current year, verifies the table, if none registry found, then inserts a new one starting from 1. Otherwise, select the last and increments by one, updating the corresponding table.
The Table:
create table GDRDOCUMENTOSEQ
(
noano NUMBER(4),
noseq NUMBER(6)
)
;
alter table GDRDOCUMENTOSEQ
add unique (NOANO);
The Trigger:
CREATE OR REPLACE TRIGGER "GDRGUIARESSARCIMENTONODOC_BIR"
BEFORE INSERT ON GDR.GDRGUIARESSARCIMENTO
FOR EACH ROW
DECLARE
lNoAno number;
lNoSeq number;
lQtd number;
begin
SELECT EXTRACT(YEAR FROM SYSDATE) into lNoAno FROM DUAL;
SELECT COUNT(0)
INTO lQtd
FROM gdr.gdrdocumentoseq ds
WHERE ds.noano = lNoAno;
IF lQtd = 0 then
lNoSeq := 1;
INSERT INTO GDR.GDRDOCUMENTOSEQ (NOANO, NOSEQ) VALUES (lNoAno, lNoSeq);
else
SELECT nvl(max(ds.noseq), 0) + 1
INTO lNoSeq
FROM gdr.gdrdocumentoseq ds
WHERE ds.noano = lNoAno;
UPDATE GDR.GDRDOCUMENTOSEQ ds
SET ds.noseq = lNoSeq
WHERE ds.noano = lNoAno;
end if;
:new.nodocumento := SUBSTR(lNoAno, 3) || lpad(lNoSeq, 6, '0');
end;
I have this code running in production from 2016. Currenty state of the table is:
NOANO NOSEQ
2017 1411
2016 237
create or replace procedure Reset_Sequence(pSeqName in varchar2) is
vLastValue number;
begin
execute immediate 'select ' || pSeqName || '.nextval from dual'
INTO vLastValue;
execute immediate 'alter sequence ' || pSeqName || ' increment by -' ||
vLastValue || ' minvalue 0';
execute immediate 'select ' || pSeqName || '.nextval from dual'
INTO vLastValue;
execute immediate 'alter sequence ' || pSeqName ||
' increment by 1 minvalue 0';
end;