Oracle 如何在动态SQL中更改序列?

Oracle 如何在动态SQL中更改序列?,oracle,plsql,sequence,dynamic-sql,Oracle,Plsql,Sequence,Dynamic Sql,我正在尝试创建一个脚本来将数据从一个数据库迁移到另一个数据库。我目前不能做的一件事是将一个序列的nextval设置为另一个DB中序列的nextval 我从user_序列中获得了值的差异,并生成了以下动态SQL语句: execute immediate 'alter sequence myseq increment by 100'; execute immediate 'select myseq.nextval from dual'; execute immediate 'alter sequen

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

我从user_序列中获得了值的差异,并生成了以下动态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;
编辑:为不清楚向所有人道歉。我实际上是在改变同一个数据库中的序列。我只从远程数据库获取要设置的值。也许没有必要提及远程数据库,因为它不会影响任何事情。我提到它只是为了解释我的目标是什么

第一步。我从远程数据库获取序列的下一部分

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;    
第二步。我使用此值生成动态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语句并在外部运行时,它们起了作用。

我不太理解您的意思,但这里有一个猜测:

我在您的代码中没有看到,但您正在谈论在另一个数据库上执行DDL(
CREATE
ALTER
等),因此我假设您正在使用数据库链接。无法使用数据库链接在另一个数据库上执行DDL。你可能想考虑一下。


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

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;

下面是一些将序列动态设置为新(更高)值的代码。我写了这篇文章,所以它将适用于模式中的任何序列

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;
该过程不需要提交,因为DDL语句发出隐式提交(实际上是两个)

您可以执行它并看到如下所示的同步值(在SQL*PLus中):

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


在18c中,Oracle增加了重新启动功能来改变顺序。简单的选择

alter sequence myseq restart;
…将序列重置为原始CREATE sequence语句中的START WITH子句指定的值。另一个选项允许我们指定一个新的起点:

alter sequence myseq restart start with 23000;
令人兴奋的是,这个新的起点可以在当前值之前或之后(在序列的常规范围内)


一个障碍是,这个新功能没有文档记录(仅用于Oracle内部使用),因此我们不应该使用它。唯一被认可的更改序列值的机制是我上面概述的机制。

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

AUTHID当前用户


声明包规范时,如果您希望它具有当前用户的凭据,我将使用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;

+一个很好的答案。抱歉,我之前不清楚:(我实际上正在尝试更改同一数据库中的序列。@[APC]谢谢,伙计!终于成功了!我希望我能给你+10!抱歉,我之前不清楚:(我实际上在尝试更改同一数据库中的序列。@[APC]是的,我没有收到任何错误消息(错误已记录)。抱歉,我之前不清楚-我实际上是在从拥有它的同一个数据库更改序列。PL/SQL过程成功完成。我正在使用10g 10.2.0.5.0-64biHmm。我只是在没有动态SQL的情况下尝试了它-它不会让我在PL/SQL块中放置任何alter语句,除非它是动态SQL。它说“遇到了符号”alter“当期望以下情况之一时…”。我的目的是在需要时运行一个过程来刷新数据。我需要一个PL/SQL块来从远程数据库获取值-它不会与子查询一起获得“alter sequence myseq increment by X”的值:@[APC]嗯,我只是在没有使用动态SQL的情况下尝试了它-它不会让我在PL/SQL块中放入任何alter语句,除非它是动态SQL。它说“遇到了符号”alter“,当需要下列之一时。。。“。我的目的是在需要时运行一个过程来刷新数据。我需要一个PL/SQL块来从远程数据库获取值-它不会使用子查询来获取“alter sequence myseq increment by X”的值。”。
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;