Лучший способ сбросить последовательность Oracle к следующему значению в существующем столбце? - PullRequest
39 голосов
/ 23 мая 2011

По какой-то причине люди в прошлом вставляли данные без использования sequence.NEXTVAL. Поэтому, когда я собираюсь использовать sequence.NEXTVAL для заполнения таблицы, я получаю нарушение PK, так как это число уже используется в таблице.

Как я могу обновить следующее значение, чтобы оно могло использоваться? Прямо сейчас я просто вставляю снова и снова, пока он не будет успешным (INSERT INTO tbl (pk) VALUES (sequence.NEXTVAL)), и это синхронизирует следующее значение.

Ответы [ 5 ]

77 голосов
/ 23 мая 2011

Вы можете временно увеличить размер кэша и сделать один фиктивный выбор, а затем сбросить размер кэша обратно до 1. Так, например,

ALTER SEQUENCE mysequence INCREMENT BY 100;

select mysequence.nextval from dual;

ALTER SEQUENCE mysequence INCREMENT BY 1;
16 голосов
/ 25 июля 2012

Эти две процедуры позволяют мне сбросить последовательность и сбросить последовательность на основе данных в таблице (извинения за соглашения о кодировании, используемые этим клиентом):

CREATE OR REPLACE PROCEDURE SET_SEQ_TO(p_name IN VARCHAR2, p_val IN NUMBER)
AS
   l_num   NUMBER;
BEGIN
   EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;

   -- Added check for 0 to avoid "ORA-04002: INCREMENT must be a non-zero integer"
   IF (p_val - l_num - 1) != 0
   THEN
      EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by ' || (p_val - l_num - 1) || ' minvalue 0';
   END IF;

   EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;

   EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by 1 ';

   DBMS_OUTPUT.put_line('Sequence ' || p_name || ' is now at ' || p_val);
END;

CREATE OR REPLACE PROCEDURE SET_SEQ_TO_DATA(seq_name IN VARCHAR2, table_name IN VARCHAR2, col_name IN VARCHAR2)
AS
   nextnum   NUMBER;
BEGIN
   EXECUTE IMMEDIATE 'SELECT MAX(' || col_name || ') + 1 AS n FROM ' || table_name INTO nextnum;

   SET_SEQ_TO(seq_name, nextnum);
END;
12 голосов
/ 23 мая 2011

Если вы можете рассчитывать на наличие периода времени, когда таблица находится в стабильном состоянии без новых вставок, это следует сделать (не проверено):

DECLARE
  last_used  NUMBER;
  curr_seq   NUMBER;
BEGIN
  SELECT MAX(pk_val) INTO last_used FROM your_table;

  LOOP
    SELECT your_seq.NEXTVAL INTO curr_seq FROM dual;
    IF curr_seq >= last_used THEN EXIT;
    END IF;
  END LOOP;
END;

Это позволяет вам вернуть последовательность в синхронизацию с таблицей, не удаляя / воссоздавая / повторно предоставляя последовательность. Он также не использует DDL, поэтому неявные фиксации не выполняются. Конечно, вам придется выследить и дать пощечину людям, которые настаивают на том, чтобы не использовать последовательность для заполнения столбца ...

10 голосов
/ 20 июня 2014

В моем случае у меня есть последовательность под названием PS_LOG_SEQ, которая имеет LAST_NUMBER = 3920.

Затем я импортировал некоторые данные из PROD на свой локальный компьютер и вставил в таблицу PS_LOG. Производственные данные имели более 20000 строк, причем последний LOG_ID (первичный ключ) был 20070. После импорта я попытался вставить новые строки в эту таблицу, но при сохранении я получил исключение, подобное этому:

ORA-00001: unique constraint (LOG.PS_LOG_PK) violated

Конечно, это связано с последовательностью PS_LOG_SEQ, связанной с таблицей PS_LOG. LAST_NUMBER сталкивался с данными, которые я импортировал, которые уже использовали следующее значение идентификатора из PS_LOG_SEQ.

Чтобы решить, что я использовал эту команду, чтобы обновить последовательность до последней \ max(LOG_ID) + 1:

alter sequence PS_LOG_SEQ restart start with 20071;

Эта команда сбрасывает значение LAST_NUMBER, и тогда я могу вставить новые строки в таблицу. Нет больше столкновения. :)

Примечание: эта alter sequence команда является новой в Oracle 12c.

9 голосов
/ 25 октября 2013

с оракулом 10,2 г:

select  level, sequence.NEXTVAL
from  dual 
connect by level <= (select max(pk) from tbl);

установит текущее значение последовательности на максимум (pk) вашей таблицы (то есть следующий вызов NEXTVAL даст вам правильный результат); если вы используете Toad, нажмите F5 для запуска оператора, а не F9, который выводит на экран выходные данные (таким образом останавливая приращение после, как правило, 500 строк). Хорошая сторона: это решение только DML, а не DDL. Только SQL и нет PL-SQL. Плохая сторона: это решение печатает максимум (pk) строк вывода, то есть обычно медленнее, чем решение ALTER SEQUENCE.

...