6

我正在尝试创建一个脚本来将数据从一个数据库迁移到另一个数据库。我目前无法做的一件事是将序列的下一个值设置为另一个数据库中序列的下一个值。

我从 user_sequences 中得到了值的差异,并生成了以下动态 SQL 语句:

execute immediate 'alter sequence myseq increment by 100';
execute immediate 'select myseq.nextval from dual';
execute immediate 'alter sequence myseq increment by 1';

commit;

但什么也没有发生。我错过了什么?如果我在过程之外运行相同的语句,它们可以正常工作:

alter sequence myseq increment by 100;
select myseq.nextval from dual;
alter sequence myseq increment by 1;

commit;

编辑:向所有不清楚的人道歉。我实际上正在改变同一个数据库中的序列。我只是从远程数据库中获取要设置的值。也许没有必要提及远程数据库,因为它不会影响事物。我只是提到它来解释我的目标是什么。

步骤 1. 我从远程数据库获取序列的 nextval。

select (select last_number
        from dba_sequences@remoteDB
        where upper(sequence_name) = upper(v_sequence_name)) - (select last_number
                                                                from user_sequences
                                                                where upper(sequence_name) = upper(v_sequence_name)) increment_by
from dual;    

第 2 步。我使用此值生成动态 SQL 语句:

execute immediate 'alter sequence myseq increment by 100';
execute immediate 'select myseq.nextval from dual';
execute immediate 'alter sequence myseq increment by 1';

commit;

没有引发错误,但什么也没发生。当我用 DBMS_OUTPUT.PUT_LINE 编写 SQL 语句并在外面运行它们时,它们就起作用了。

4

4 回答 4

7

这是一些将序列动态设置为新(更高)值的代码。我已经写了这个,所以它适用于您架构中的任何序列。

create or replace procedure resync_seq
    (p_seq_name in user_sequences.sequence_name%type)
is
    local_val pls_integer;
    remote_val pls_integer;
    diff pls_integer;
begin
    execute immediate 'select '|| p_seq_name ||'.nextval from dual'
           into local_val;
    select last_number into remote_val
    from user_sequences@remote_db
    where sequence_name = p_seq_name ;
    diff := remote_val - local_val;

    if diff > 0
    then
        execute immediate 'alter sequence  '|| p_seq_name ||' increment by ' ||to_char(diff);
        execute immediate 'select '|| p_seq_name ||'.nextval from dual'
           into local_val;
        execute immediate 'alter sequence  '|| p_seq_name ||' increment by 1';
    end if;

end;

该过程不需要 COMMIT,因为 DDL 语句发出隐式提交(实际上是两个)。

您可以执行它并查看这样的同步值(在 SQL*PLus 中):

exec resync_seq('MYSEQ')
select myseq.currval
from dual

顺便说一句,重置序列(到其原始起始值或不同的较低值)的唯一方法是删除并重新创建序列。


在 18c 中,Oracle 向 ALTER SEQUENCE 添加了 RESTART 功能。直接的选择...

alter sequence myseq restart;

...将序列重置为原始 CREATE SEQUENCE 语句中的 START WITH 子句指定的值。另一个选项允许我们指定一个的起点:

alter sequence myseq restart start with 23000;

令人兴奋的是,这个新的起点可以在当前值之前或之后(在序列的通常范围内)。

一个问题是这个新功能没有被记录(仅用于 Oracle 的内部使用),所以我们不应该使用它。在 20c 中仍然如此。改变序列值的唯一被批准的机制是我上面概述的。

于 2012-04-30T14:45:47.903 回答
3

我不太明白你的意思,但这是一个疯狂的猜测:

我在您的代码中没有看到它,但您正在谈论在另一个数据库上执行 DDL(等),所以我假设您正在使用数据库链接CREATE不能使用数据库链接在另一个数据库上执行 DDL。您可能要考虑这一点。ALTER


在您提供的信息之后,这可能就是您需要的。如果你想设置序列的当前值,你不能,根据这个文档,你需要删除/创建:

declare
  ln_lastNumber DBA_SEQUENCES.LAST_NUMBER%type;
  lv_sequenceName DBA_SEQUENCES.SEQUENCE_NAME%type := 'MYSEQ';
begin
  select LAST_NUMBER
  into ln_lastNumber
  from DBA_SEQUENCES--or @remote_db;
  where
    --Your predicates;

  execute immediate 'drop sequence ' || lv_sequenceName;
  execute immediate 'create sequence ' || lv_sequenceName || ' starts with ' || ln_lastNumber;
exception
  when no_data_found then
    dbms_output.put_line('No sequence found!'); -- Or log somehow.
    raise;
  when others then
    raise;
end;
于 2012-04-30T11:53:26.437 回答
0

此外,动态 SQL 包中的 DDL 需要

AUTHID CURRENT_USER

声明包规范时,如果您希望它具有当前用户的凭据

于 2012-04-30T19:09:51.027 回答
0

我把APC提供的代码修改如下:

create or replace procedure resync_seq
    (p_seq_name in user_sequences.sequence_name%type, 
     p_table_name in user_tables.table_name%type, 
     p_pk in user_cons_columns.column_name%type)
is
     local_val pls_integer;
     remote_val pls_integer;
     diff pls_integer;
begin
      execute immediate 'select '|| p_seq_name ||'.nextval from dual'
       into local_val;

       execute immediate 'select max('||p_pk||') from '||p_table_name ||' ' 
       into remote_val ;

       diff := remote_val - local_val;

       if diff > 0
          then
          execute immediate 'alter sequence  '|| p_seq_name ||' increment by ' ||to_char(diff);
          execute immediate 'select '|| p_seq_name ||'.nextval from dual'
       into local_val;
          execute immediate 'alter sequence  '|| p_seq_name ||' increment by 1';
       end if;

 end;
于 2015-10-08T17:34:39.790 回答