Inquiring if there is a way to automatically set START WITH value of a sequence.
I want to drop and create the sequence since there is an error when inserting a row, problem is it affected all sequences hence we are unable to insert a row on multiple tables.
You can create small scripts like this when you create it :
-- Created on 18.10.2022 by ALI.FIDANLI
declare
-- Local variables here
i integer;
BEGIN
select max(cb_refno)+1 into i from dwh.customer_fact ;
dbms_output.put_line(i);
execute immediate
' create sequence SEQ_TEST minvalue 1 maxvalue 9999999999999999999999999999 start with '
|| i ||
' increment by 1 cache 20';
END;
Related
In case, when newly created schema with empty tables and sequences set to initial values, is populated by inserts with IDs already set, sequences will stay unmodified (as expected).
How to correct sequences so they will return next available index (ID)?
select SEQ_TABLE.nextval from dual;
If you are running at least Oracle 19c, you can do this without dropping the sequence quite easily:
Alter sequence my_sequence restart minvalue 100;
You can obviously call this in PL/SQL using dynamic SQL (like you’re doing in your proposed answer)
I came up with the following code:
DECLARE
var NUMBER;
BEGIN
execute immediate 'drop sequence SEQ_TABLE';
SELECT MAX(ID) INTO var FROM TABLE;
IF var IS NULL THEN
var := 1;
ELSE
var := var + 1;
END IF;
execute immediate 'create sequence SEQ_TABLE
start with ' || var || '
increment by 1
maxvalue 999999999999999
cache 5
order';
commit;
END;
/
there is a SEQUENCE named test_seq.
SELECT TO_NUMBER('-'||TO_CHAR(test_seq.currval)) from DUAL; // can work
ALTER SEQUENCE test_seq INCREMENT BY TO_NUMBER('-'||TO_CHAR(test_seq.currval)); // wrong!
can I use function(like: TO_NUMBER() or TO_CHAR()) in ALTER statement?
No, you cannot. The railroad diagram disallows it.
You can accomplish it with EXECUTE IMMEDIATE statement in a block as shown.
DECLARE
next_increment NUMBER := TO_NUMBER('-'||TO_CHAR(test_seq.currval));
BEGIN
EXECUTE IMMEDIATE 'ALTER SEQUENCE test_seq INCREMENT BY '||next_increment;
END;
/
But make sure -
you have run test_seq.nextval atleast once before you execute this
block.
Use the sequence increment with caution . You might get the following
error if it goes below MINVALUE of the sequence.
ORA-08004: sequence TEST_SEQ.NEXTVAL goes below MINVALUE and cannot be instantiated
I have a sequence in my oracle database and set the maximum number to 99999. I like to automate resetting to 10000 before it reach to 99999. Below is my script of myseq.
create sequence myseq
minvalue 1
maxvalue 99999
increment by 1
start with 10000;
The simplest way is to define sequence as CYCLE:
create sequence myseq minvalue 10000 maxvalue 99999 increment by 1 CYCLE;
It will start from minvalue again when reaches maxvalue, i.e. generate numbers in cycle.
Oracle spec says:
CYCLE - Specify CYCLE to indicate that the sequence continues to generate values after reaching either its maximum or minimum value.
After an ascending sequence reaches its maximum value, it generates
its minimum value. After a descending sequence reaches its minimum, it
generates its maximum value.
If you are in 12c, you can use the IDENTITY column and get rid off the SEQUENCE mechanism.
If you are not on 12c, assuming that your primary key is populated via sequence using a trigger. What you can do is :
Create a trigger with the logic to reset the sequence back to normal,
i.e. after every time you purge the table, the sequence would START
WITH 1 and INCREMENT BY 1. using ALTER SEQUENCE.
The sequence logic part using alter statement (Thanks to Tom Kyte for
this) :
create or replace
procedure reset_sequence(p_seq in varchar2)
is
l_value number;
begin
-- Select the next value of the sequence
execute immediate
'select ' || p_seq ||
'.nextval from dual' INTO l_value;
-- Set a negative increment for the sequence,
-- with value = the current value of the sequence
execute immediate
'alter sequence ' || p_seq ||
' increment by -' || l_value || ' minvalue 0';
-- Select once from the sequence, to
-- take its current value back to 0
execute immediate
'select ' || p_seq ||
'.nextval from dual' INTO l_value;
-- Set the increment back to 1
execute immediate
'alter sequence ' || p_seq ||
' increment by 1 minvalue 0';
end;
/
Update : Don't know what I was thinking initially. Based on the other answer, you need ALTER SEQUENCE to TURN ON CYCLE.
ALTER SEQUENCE sequence_name CYCLE;
I am using a trigger to reset a sequence every year,
but there is some issue when calling a procedure into the triggers
CREATE OR REPLACE TRIGGER t_dmd_pk
BEFORE INSERT
ON S_DEMANDE
FOR EACH ROW
BEGIN
IF (TO_CHAR (SYSDATE, 'dd') = '16' AND TO_CHAR (SYSDATE, 'mm') = '12')
THEN
reset_seq ('SEQ_ID_DMD');
END IF;
SELECT SEQ_ID_DMD.NEXTVAL || TO_CHAR (SYSDATE, 'yyyy')
INTO :new.DMD_ID
FROM DUAL;
END;
/
and that's my procedure
CREATE OR REPLACE PROCEDURE reset_seq (p_seq_name IN VARCHAR2)
IS
l_val NUMBER;
BEGIN
EXECUTE IMMEDIATE 'select ' || p_seq_name || '.nextval from dual'
INTO l_val;
EXECUTE IMMEDIATE
'alter sequence ' || p_seq_name || ' increment by -' || l_val;
END;
/
The trigger is executed inside an INSERT statement, and the trigger call a procedure that tries to commit the transaction (ALTER SEQUENCE is a DDL statatement, so it is auto-commited).
To ensure statement atomicity the transaction can only be commited when the last statement is finalized. So it is not possible to commit the current transaction inside a trigger.
But you can execute your trigger or procedure as an autonomous transaction (Oracle opens a new transaction and executes the code of your trigger or porcedure inside this new transaction).
See this link for more details: http://www.oracle-base.com/articles/misc/autonomous-transactions.php
But remember:
the autonomous transaction cannot see your still uncommited data, and
if you finally rollback your current transaction (after the execution of the trigger and the commit of the autonomous transaction) the inserted tuples will be rolled back, but the autonomous transaction will not be rolled back.
Your procedure doesn't work the way you think it should.. If your sequence last value was 10, then you are altering the sequence to increment by -10 every time it is called. I am guessing the first time you execute it, you get an ORA-08004 because your minvalue is probably 1 and it would be trying to return 0 (which isn't allowed). Even if that didn't error, the next time you called it would, as it would try to return -10 in my example. What I believe you really want is:
CREATE OR REPLACE PROCEDURE reset_seq (p_seq_name IN VARCHAR2)
IS
l_val NUMBER;
BEGIN
-- Get Current Value of Sequence
EXECUTE IMMEDIATE 'select ' || p_seq_name || '.nextval from dual'
INTO l_val;
-- Alter to sequence to allow to go to 0 and decrease by current value
EXECUTE IMMEDIATE
'alter sequence ' || p_seq_name || ' minvalue 0 increment by -' || l_val;
-- Get value from sequence again (should set seq to 0)
EXECUTE IMMEDIATE 'select ' || p_seq_name || '.nextval from dual'
INTO l_val;
-- Alter sequence to increase by 1 again
EXECUTE IMMEDIATE
'alter sequence ' || p_seq_name || ' increment by 1';
END;
This allows the sequence to be 0 (which you need if you want the next call to return 1), sets it to 0, then changes it back to increment by 1 with each successive call. However, it is probably much easier to just drop and recreate the sequence.
The real question though, is why you would ever want to do this. This looks like bad design. A sequence is just supposed to return a unique number. Nothing more, nothing less. There should be no meaning behind the number, and it sure seems like you are trying to assign meaning here. Sequences don't guarantee your rows will be inserted in order and don't guarantee there won't be gaps, they just provide a unique number. Concatenating the year on the end of this unique number makes this design more suspect.
i found a solution, i used dbms_job instead of trigger, that works fine for me
Here's a simple question about how to create a sequence with variables.
I wanna create a sequence using a combination of system time as it's start value. How should I do that.
Here's what I wrote:
DECLARE
SQS number :=(sysdate - to_date('01-JAN-1970','DD-MON-YYYY')) * (864000);
sql_stmt varchar2(200);
BEGIN
sql_stmt := 'create SEQUENCE XXXX_id_seq MINVALUE 100000 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH :1 CACHE 500 NOORDER CYCLE';
EXECUTE IMMEDIATE sql_stmt using SQS;
END;
but it says invalid num.
I know it's a noobie question. but I really need help here.
You can't bind variables in DDL statements (I ain't got doc or a good reason why, but quite a few references mentioning the fact), you've got to concatenate all in one.
DECLARE
SQS number :=(sysdate - to_date('01-JANV.-1970','DD-MON-YYYY')) * (864000);
sql_stmt varchar2(200);
BEGIN
sql_stmt := 'create SEQUENCE XXXX_id_seq MINVALUE 100000 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH '||SQS||' CACHE 500 NOORDER CYCLE';
EXECUTE IMMEDIATE sql_stmt ;
END;