Как автоматически сбрасывать значение последовательности на 0 каждый год в Oracle 10g? - PullRequest
6 голосов
/ 03 июня 2009

Как и в вопросе, как автоматически сбрасывать значение последовательности Oracle обратно на 0 каждый год в Oracle 10g?

Я использую последовательность для генерации идентификатора в формате YYYY<sequence value>, и значение последовательности необходимо сбрасывать в 0 каждый год.

YYYY получено из Java и объединено со значением последовательности из Oracle. Формат идентификатора не может быть изменен из-за внешних требований третьих сторон. Спасибо за любую помощь заранее.

Ответы [ 6 ]

6 голосов
/ 03 июня 2009

Последовательности не предназначены для сброса. Но в некоторых случаях требуется сброс последовательности, например, при настройке тестовых данных или слиянии производственных данных обратно в тестовую среду. Этот вид деятельности не обычно выполняется в производстве.

Если этот тип операции будет запущен в производство, его необходимо тщательно протестировать. (Что вызывает наибольшую обеспокоенность, так это вероятность того, что процедура сброса будет случайно выполнена в неподходящее время, например, в середине года.

Удаление и воссоздание последовательности является одним из подходов. Как операция, это довольно просто, насколько SEQUENCE идет:

    DROP SEQUENCE MY_SEQ;
    CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 0;

[EDIT] Как правильно указывает Мэтью Уотсон, каждый оператор DDL (такой как DROP, CREATE, ALTER) будет вызывать неявную фиксацию. [/ EDIT]

Но все привилегии, предоставленные в ПОСЛЕДОВАТЕЛЬНОСТИ, будут отброшены, поэтому их необходимо будет повторно предоставить. Любые объекты, которые ссылаются на последовательность, будут признаны недействительными. Чтобы сделать это более обобщенным, вам необходимо сохранить привилегии (перед удалением последовательности), а затем повторно предоставить их.

Второй подход - ИЗМЕНИТЬ существующую ПОСЛЕДОВАТЕЛЬНОСТЬ, не удаляя и не воссоздавая ее. Сброс последовательности может быть выполнен путем изменения значения INCREMENT на отрицательное значение (разница между текущим значением и 0), а затем сделать ровно один .NEXTVAL, чтобы установить текущее значение на 0, а затем изменить значение INCREMENT обратно на 1. Я использовал этот же подход ранее (вручную, в тестовой среде), чтобы установить для последовательности также большее значение.

Конечно, для правильной работы вам необходимо обеспечить , чтобы другие сеансы не ссылались на последовательность во время выполнения этой операции. Дополнительный .NEXTVAL в неправильный момент испортит сброс. (ПРИМЕЧАНИЕ: добиться этого на стороне базы данных будет сложно, если приложение подключается как владелец последовательности, а не как отдельный пользователь.)

Чтобы это происходило каждый год, вам нужно было бы запланировать работу. Сброс последовательности должен быть согласован со сбросом YYYY-части вашего идентификатора.

Вот пример:

http://www.jaredstill.com/content/reset-sequence.html

[EDIT]

UNTESTED заполнитель для одного возможного дизайна блока PL / SQL для сброса последовательности

    declare
      pragma autonomous_transaction;
      ln_increment       number;
      ln_curr_val        number;
      ln_reset_increment number;
      ln_reset_val       number;
    begin

      -- save the current INCREMENT value for the sequence
      select increment_by
        into ln_increment
        from user_sequences
       where sequence_name = 'MY_SEQ';

      -- determine the increment value required to reset the sequence
      -- from the next fetched value to 0
      select -1 - MY_SEQ.nextval into ln_reset_increment from dual;

      -- fetch the next value (to make it the current value)
      select MY_SEQ.nextval into ln_curr from dual;

      -- change the increment value of the sequence to 
      EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by '
        || ln_reset_increment ||' minvalue 0';

      -- advance the sequence to set it to 0
      select MY_SEQ.nextval into ln_reset_val from dual;

      -- set increment back to the previous(ly saved) value
      EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by '
        || ln_increment ;
    end;
    /

ПРИМЕЧАНИЯ:

  • как лучше защитить последовательность от доступа во время ее сброса, переименовать ее?
  • Несколько тестовых случаев для проработки здесь.
  • Первый проход, проверьте нормативные случаи положительной, возрастающей последовательности с шагом 1.
  • будет ли лучшим подходом создать новую SEQUENCE, добавить разрешения, переименовать существующие и новые последовательности, а затем пересобрать зависимости?
5 голосов
/ 03 июня 2009

Просто добавлю это как идею:

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

  1. Создать одну последовательность.

  2. Создайте справочную таблицу с одной строкой для каждого года, например

    ЛЕТ (год НОМЕР (4,0) ПЕРВИЧНЫЙ КЛЮЧ, начальное_значение НОМЕР)

  3. Когда вы получаете NEXTVAL из последовательности, вам нужно вычесть starting_value при запросе из таблицы YEARS за текущий год. Если год не найден, должна быть вставлена ​​новая строка (т. Е. При первом запуске процесса в любом данном году будет добавлено новое значение).

например. функция, например get_year_starting_value (pn_year IN NUMBER) RETURN NUMBER может запросить эту таблицу и вернуть starting_value для данного года; если он получает NO_DATA_FOUND, он может вызвать процедуру для вставки его, используя NEXTVAL из последовательности (зафиксировано в автономной транзакции, чтобы новое значение было немедленно доступно для других сеансов, и чтобы функция не вызывала сбоев из-за побочного эффекта)

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

4 голосов
/ 03 июня 2009

Используйте работу, чтобы сделать трюк. Сначала создайте хранимую процедуру для сброса вашей последовательности (я обычно использую решение DROP / CREATE, но вы можете использовать трюк spencer7593 ):

CREATE OR REPLACE PROCEDURE my_seq_reset AS
BEGIN
    EXECUTE IMMEDIATE 'DROP SEQUENCE my_seq';
    EXECUTE IMMEDIATE
      'CREATE SEQUENCE my_seq' ||
      '  MINVALUE 1 ' ||
      '  MAXVALUE 999999 ' ||
      '  START WITH 1 ' ||
      '  INCREMENT BY 1 ' ||
      '  NOCACHE';
END;

Затем создайте задание ( см. Здесь для справки):

BEGIN
  dbms_scheduler.create_job(
    job_name        => 'job$my_seq_reset',
    job_type        => 'STORED_PROCEDURE',
    job_action      => 'my_seq_reset',
    start_date      => TO_DATE('01-01-09', 'DD-MM-RR'),
    repeat_interval => 'FREQ=YEARLY;BYDATE=0101',
    enabled         => TRUE,
    auto_drop       => FALSE,
    comments        => 'My sequence yearly reset job.'
  );
END;

Вы сделали.

1 голос
/ 03 июня 2009

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

На ум приходят 2 мысли.

  1. В 12 часов утра первого сброса последовательности, это сложно, потому что вам нужно убедиться, что вы взяли любой код.
  2. Создайте последовательность для каждого года, возможно, даже включите ее в свой код, чтобы иметь возможность создать последовательность, а затем динамически вызовите правильную последовательность для года.

Я бы предпочел вариант 2, так как он не пытается делать что-то причудливое и всегда будет работать без сбоев, любые варианты, пытающиеся манипулировать самой последовательностью, обязательно укусят вас.

0 голосов
/ 27 сентября 2017

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

Таблица Таблица :

create table GDRDOCUMENTOSEQ
(
  noano NUMBER(4),
  noseq NUMBER(6)
)
;
alter table GDRDOCUMENTOSEQ
  add unique (NOANO);

Триггер :

CREATE OR REPLACE TRIGGER "GDRGUIARESSARCIMENTONODOC_BIR"
BEFORE INSERT ON GDR.GDRGUIARESSARCIMENTO
FOR EACH ROW

DECLARE
  lNoAno number;
  lNoSeq number;
  lQtd   number;
begin

  SELECT EXTRACT(YEAR FROM SYSDATE) into lNoAno FROM DUAL;

  SELECT COUNT(0)
    INTO lQtd
    FROM gdr.gdrdocumentoseq ds
   WHERE ds.noano = lNoAno;

  IF lQtd = 0 then
    lNoSeq := 1;
    INSERT INTO GDR.GDRDOCUMENTOSEQ (NOANO, NOSEQ) VALUES (lNoAno, lNoSeq);
  else
    SELECT nvl(max(ds.noseq), 0) + 1
      INTO lNoSeq
      FROM gdr.gdrdocumentoseq ds
     WHERE ds.noano = lNoAno;

    UPDATE GDR.GDRDOCUMENTOSEQ ds
       SET ds.noseq = lNoSeq
     WHERE ds.noano = lNoAno;
  end if;

  :new.nodocumento := SUBSTR(lNoAno, 3) || lpad(lNoSeq, 6, '0');

end;

Этот код запущен в производство с 2016 года. Текущее состояние таблицы:

NOANO   NOSEQ   
2017    1411    
2016    237
0 голосов
/ 03 июня 2009

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

http://www.psoug.org/reference/OLD/sequences.html?PHPSESSID=5949da378678fa6d24b6fcc6eaae9888

Мой подход будет:

  1. создать таблицу с годом и начальной последовательностью для этого года (назовем эту таблицу year_seed)

  2. создайте процедуру, которая получает год, проверяет таблицу year_seed и, если это первая проверка для года, генерирует регистр с начальной последовательностью. Эта процедура также должна возвращать последовательность минус начальная последовательность для года.

Возможно, это не так просто, но я думаю, что это лучшее решение. Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...