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

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

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

5 ответов


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

ALTER SEQUENCE mysequence INCREMENT BY 100;

select mysequence.nextval from dual;

ALTER SEQUENCE mysequence INCREMENT BY 1;

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

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;

с oracle 10.2 g:

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

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


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

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, поэтому неявные коммиты не выполняются. Конечно, вам придется выследить и ударить людей, которые настаивают на том, чтобы не использовать последовательность для заполнения столбца...


в моем случае у меня есть последовательность называется 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 таблица. The LAST_NUMBER столкнулся с данными, которые я импортировал, которые уже использовали следующее значение ID из PS_LOG_SEQ.

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

alter sequence PS_LOG_SEQ restart start with 20071;

эта команда сбросит LAST_NUMBER value, и я мог бы вставить новые строки в таблицу. Больше никаких столкновений. :)

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