How do I reset a sequence in Oracle?

后端 未结 18 1256
天命终不由人
天命终不由人 2020-11-22 05:09

In PostgreSQL, I can do something like this:

ALTER SEQUENCE serial RESTART WITH 0;

Is there an Oracle equivalent?

相关标签:
18条回答
  • 2020-11-22 05:44

    This is my approach:

    1. drop the sequence
    2. recreate it

    Example:

    --Drop sequence
    
    DROP SEQUENCE MY_SEQ;
    
    -- Create sequence 
    
    create sequence MY_SEQ
    minvalue 1
    maxvalue 999999999999999999999
    start with 1
    increment by 1
    cache 20;
    
    0 讨论(0)
  • 2020-11-22 05:46

    Here's how to make all auto-increment sequences match actual data:

    1. Create a procedure to enforce next value as was already described in this thread:

      CREATE OR REPLACE PROCEDURE Reset_Sequence(
          P_Seq_Name IN VARCHAR2,
          P_Val      IN NUMBER DEFAULT 0)
      IS
        L_Current    NUMBER                      := 0;
        L_Difference NUMBER                      := 0;
        L_Minvalue User_Sequences.Min_Value%Type := 0;
      BEGIN
        SELECT Min_Value
        INTO L_Minvalue
        FROM User_Sequences
        WHERE Sequence_Name = P_Seq_Name;
        EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Current;
        IF P_Val        < L_Minvalue THEN
          L_Difference := L_Minvalue - L_Current;
        ELSE
          L_Difference := P_Val - L_Current;
        END IF;
        IF L_Difference = 0 THEN
          RETURN;
        END IF;
        EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by ' || L_Difference || ' minvalue ' || L_Minvalue;
        EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Difference;
        EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by 1 minvalue ' || L_Minvalue;
      END Reset_Sequence;
      
    2. Create another procedure to reconcile all sequences with actual content:

      CREATE OR REPLACE PROCEDURE RESET_USER_SEQUENCES_TO_DATA
      IS
        STMT CLOB;
      BEGIN
        SELECT 'select ''BEGIN'' || chr(10) || x || chr(10) || ''END;'' FROM (select listagg(x, chr(10)) within group (order by null) x FROM ('
          || X
          || '))'
        INTO STMT
        FROM
          (SELECT LISTAGG(X, ' union ') WITHIN GROUP (
          ORDER BY NULL) X
          FROM
            (SELECT CHR(10)
              || 'select ''Reset_Sequence('''''
              || SEQ_NAME
              || ''''','' || coalesce(max('
              || COL_NAME
              || '), 0) || '');'' x from '
              || TABLE_NAME X
            FROM
              (SELECT TABLE_NAME,
                REGEXP_SUBSTR(WTEXT, 'NEW\.(\S*) IS NULL',1,1,'i',1) COL_NAME,
                REGEXP_SUBSTR(BTEXT, '(\.|\s)([a-z_]*)\.nextval',1,1,'i',2) SEQ_NAME
              FROM USER_TRIGGERS
              LEFT JOIN
                (SELECT NAME BNAME,
                  TEXT BTEXT
                FROM USER_SOURCE
                WHERE TYPE = 'TRIGGER'
                AND UPPER(TEXT) LIKE '%NEXTVAL%'
                )
              ON BNAME = TRIGGER_NAME
              LEFT JOIN
                (SELECT NAME WNAME,
                  TEXT WTEXT
                FROM USER_SOURCE
                WHERE TYPE = 'TRIGGER'
                AND UPPER(TEXT) LIKE '%IS NULL%'
                )
              ON WNAME             = TRIGGER_NAME
              WHERE TRIGGER_TYPE   = 'BEFORE EACH ROW'
              AND TRIGGERING_EVENT = 'INSERT'
              )
            )
          ) ;
        EXECUTE IMMEDIATE STMT INTO STMT;
        --dbms_output.put_line(stmt);
        EXECUTE IMMEDIATE STMT;
      END RESET_USER_SEQUENCES_TO_DATA;
      

    NOTES:

    1. Procedure extracts names from trigger code and does not depend on naming conventions
    2. To check generated code before execution, switch comments on last two lines
    0 讨论(0)
  • 2020-11-22 05:49

    Altering the sequence's INCREMENT value, incrementing it, and then altering it back is pretty painless, plus you have the added benefit of not having to re-establish all of the grants as you would had you dropped/recreated the sequence.

    0 讨论(0)
  • 2020-11-22 05:50
    alter sequence serial restart start with 1;
    

    This feature was officially added in 18c but is unofficially available since 12.1.

    It is arguably safe to use this undocumented feature in 12.1. Even though the syntax is not included in the official documentation, it is generated by the Oracle package DBMS_METADATA_DIFF. I've used it several times on production systems. However, I created an Oracle Service request and they verified that it's not a documentation bug, the feature is truly unsupported.

    In 18c, the feature does not appear in the SQL Language Syntax, but is included in the Database Administrator's Guide.

    0 讨论(0)
  • 2020-11-22 05:51

    A true restart is not possible AFAIK. (Please correct me if I'm wrong!).

    However, if you want to set it to 0, you can just delete and recreate it.

    If you want to set it to a specific value, you can set the INCREMENT to a negative value and get the next value.

    That is, if your sequence is at 500, you can set it to 100 via

    ALTER SEQUENCE serial INCREMENT BY -400;
    SELECT serial.NEXTVAL FROM dual;
    ALTER SEQUENCE serial INCREMENT BY 1;
    
    0 讨论(0)
  • 2020-11-22 05:52

    There is another way to reset a sequence in Oracle: set the maxvalue and cycle properties. When the nextval of the sequence hits the maxvalue, if the cycle property is set then it will begin again from the minvalue of the sequence.

    The advantage of this method compared to setting a negative increment by is the sequence can continue to be used while the reset process runs, reducing the chance you need to take some form of outage to do the reset.

    The value for maxvalue has to be greater than the current nextval, so the procedure below includes an optional parameter allowing a buffer in case the sequence is accessed again between selecting the nextval in the procedure and setting the cycle property.

    create sequence s start with 1 increment by 1;
    
    select s.nextval from dual
    connect by level <= 20;
    
       NEXTVAL
    ----------
             1 
    ...
            20
    
    create or replace procedure reset_sequence ( i_buffer in pls_integer default 0)
    as
      maxval pls_integer;
    begin
    
      maxval := s.nextval + greatest(i_buffer, 0); --ensure we don't go backwards!
      execute immediate 'alter sequence s cycle minvalue 0 maxvalue ' || maxval;
      maxval := s.nextval;
      execute immediate 'alter sequence s nocycle maxvalue 99999999999999';
    
    end;
    /
    show errors
    
    exec reset_sequence;
    
    select s.nextval from dual;
    
       NEXTVAL
    ----------
             1 
    

    The procedure as stands still allows the possibility that another session will fetch the value 0, which may or may not be an issue for you. If it is, you could always:

    • Set minvalue 1 in the first alter
    • Exclude the second nextval fetch
    • Move the statement to set the nocycle property into another procedure, to be run at a later date (assuming you want to do this).
    0 讨论(0)
提交回复
热议问题