Oracle 9 - Сброс последовательности в соответствии с состоянием таблицы - PullRequest
18 голосов
/ 15 сентября 2009

У меня есть последовательность, используемая для заполнения моих первичных ключей (на основе целых чисел) в таблице оракулов.

Похоже, эта последовательность не всегда использовалась для вставки новых значений в таблицу. Как вернуть последовательность в соответствие с фактическими значениями в таблице?

Ответы [ 6 ]

16 голосов
/ 15 сентября 2009

Если ID - это имя вашего столбца PK, а PK_SEQ - это имя вашей последовательности:

  1. Найти значение наибольшего значения PK ВЫБЕРИТЕ МАКС. (ID) ИЗ tableName

  2. Найти значение следующего PK_SEQ по ВЫБЕРИТЕ PK_SEQ.NEXTVAL ОТ ДВОЙНОГО

  3. Если # 2> # 1, то ничего не должно быть сделано, если вы относитесь к этим значения как истинные суррогатные ключи
  4. В противном случае измените последовательность на перейти к максимальному идентификатору с помощью ALTER SEQUENCE PK_SEQ INCREMENT BY [# 1 value - # 2 значение]
  5. Повысить последовательность с помощью SELECT PK_SEQ.NEXTVAL FROM DUAL

  6. Сброс значения приращения последовательности до 1 от ALTER SEQUENCE PK_SEQ INCREMENT BY 1

Все это предполагает, что у вас нет новых вставок в таблицу, пока вы делаете это ...

11 голосов
/ 15 сентября 2009

Короче, игра это:

-- Current sequence value is 1000

ALTER SEQUENCE x INCREMENT BY -999;
Sequence altered.

SELECT X.NEXTVAL FROM DUAL;
1

ALTER SEQUENCE x INCREMENT BY 1;
Sequence altered.

Вы можете получить максимальное значение последовательности, используемое в вашей таблице, выполнить математические вычисления и соответствующим образом обновить последовательность.

8 голосов
/ 10 апреля 2013
Declare
  difference INTEGER;
  sqlstmt varchar2(255);
  sequenceValue Number;
begin
sqlstmt := 'ALTER SEQUENCE YOURSEQUENCE INCREMENT BY ';
select YOURSEQUENCE.NEXTVAL into sequenceValue from dual;
select  (nvl(Max(YOURID),0) - sequenceValue)+1 into difference from YOURTABLE;
if difference > 0 then
  EXECUTE IMMEDIATE sqlstmt || difference;
  select  YOURSEQUENCE.NEXTVAL INTO sequenceValue from dual;
  EXECUTE IMMEDIATE sqlstmt || 1;
end if;
end;
5 голосов
/ 26 мая 2014

Я создал этот сценарий, так как не нашел в сети сценарий, который динамически устанавливает все моих последовательностей на текущий максимальный идентификатор. Протестировано на Oracle 11.2.0.4.

DECLARE
  difference         INTEGER;
  sqlstmt            VARCHAR2(255) ;
  sqlstmt2           VARCHAR2(255) ;
  sqlstmt3           VARCHAR2(255) ;
  sequenceValue      NUMBER;
  sequencename       VARCHAR2(30) ;
  sequencelastnumber INTEGER;
  CURSOR allseq
  IS
     SELECT sequence_name, last_number FROM user_sequences ORDER BY sequence_name;
BEGIN
  DBMS_OUTPUT.enable(32000) ;
  OPEN allseq;
  LOOP
    FETCH allseq INTO sequencename, sequencelastnumber;
    EXIT
  WHEN allseq%NOTFOUND;
    sqlstmt  := 'ALTER SEQUENCE ' || sequencename || ' INCREMENT BY ';
    --Assuming: <tablename>_id is <sequencename>
    sqlstmt2 := 'select (nvl(Max(ID),0) - :1)+1 from ' || SUBSTR(sequencename, 1, LENGTH(sequencename) - 3) ;
    --DBMS_OUTPUT.PUT_LINE(sqlstmt2);
    --Attention: makes use of user_sequences.last_number --> possible cache problems!
    EXECUTE IMMEDIATE sqlstmt2 INTO difference USING sequencelastnumber;
    IF difference > 0 THEN
      DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt || difference) ;
      EXECUTE IMMEDIATE sqlstmt || difference;
      sqlstmt3 := 'SELECT ' || sequencename ||'.NEXTVAL from dual';
      DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt3 || ' INTO sequenceValue') ;
      EXECUTE IMMEDIATE sqlstmt3 INTO sequenceValue;
      DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt || 1) ;
      EXECUTE IMMEDIATE sqlstmt || 1;
      DBMS_OUTPUT.PUT_LINE('') ;
    END IF;
  END LOOP;
  CLOSE allseq;
END;
0 голосов
/ 07 августа 2017

Добавление до https://stackoverflow.com/a/15929548/1737973,, но без обращения к SEQUENCENAME.NEXTVAL, следовательно, не приводит к одной позиции над ним должно быть:

DECLARE
  difference INTEGER;
  alter_sequence_statement VARCHAR2 (255);
  sequence_value NUMBER;
BEGIN
  --   Base for the statement that will set the sequence value.
  alter_sequence_statement :=
      'ALTER SEQUENCE SEQUENCENAME INCREMENT BY ';

  --   Fetch current last sequence value used.
  SELECT
    --   You could maybe want to make some further computations just
    -- below if the sequence is using caching.
    last_number
  INTO sequence_value
  FROM all_sequences
  WHERE sequence_owner = 'SEQUENCEOWNER' AND sequence_name = 'SEQUENCENAME';

  --   Compute the difference.
  SELECT max(id) - sequence_value + 1 INTO difference
  FROM SCHEMANAME.TABLENAME;

  IF difference <> 0 THEN
    --   Set the increment to a big offset that puts the sequence near
    -- its proper value.
    EXECUTE IMMEDIATE alter_sequence_statement || difference;

    --   This 'sequence_value' will be ignored, on purpose.
    SELECT SEQUENCENAME.NEXTVAL INTO sequence_value FROM dual;

    --   Resume the normal pace of incrementing one by one.
    EXECUTE IMMEDIATE alter_sequence_statement || 1;
  END IF;
END;

Отказ от ответственности: если в последовательности используется кэширование (all_sequences.cache_size установлено на значение больше 0), вы, вероятно, захотите принять это во внимание на шаге Вычислить разницу .

Документация Oracle для all sequences ... .

0 голосов
/ 15 сентября 2009

В некоторых случаях вам может быть проще просто получить текущее максимальное значение, а затем

drop sequence x;
create sequence x start with {current max + 1};

Приложение будет сломано после того, как вы сделаете сброс. Но это не позволит никому вставлять строки в течение этого периода, и создание последовательности происходит быстро. Убедитесь, что вы воссоздаете любые разрешения на последовательность, так как они будут отброшены, когда последовательность будет. И вы можете вручную перекомпилировать любой plsql, который зависит от последовательности.

...