【问题标题】:How can I alter a sequence in dynamic SQL?如何更改动态 SQL 中的序列?
【发布时间】:2012-05-10 02:49:32
【问题描述】:

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

我从 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 步。我从远程数据库获取序列的下一个值。

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 语句并在外部运行它们时,它们起作用了。

【问题讨论】:

  • @[APC] 是的,我没有收到任何错误消息(记录了错误)。抱歉,我之前不清楚 - 我实际上是从拥有它的同一个数据库中更改序列。 PL/SQL 过程成功完成。我正在使用 10g 10.2.0.5.0 - 64bi
  • 嗯,我只是在没有动态 SQL 的情况下尝试了它 - 我不会在 PL/SQL 块中放入任何更改语句,除非它是动态 SQL。它说“在期待以下之一时遇到符号“ALTER”......”。我的目的是有一个程序可以在需要时运行以刷新数据。而且我需要一个 PL/SQL 块来从远程 DB 中获取值——它不能与子查询一起使用来获取“alter sequence myseq increment by X”的值:
  • @[APC] 嗯,我只是在没有动态 SQL 的情况下尝试了它 - 我不会在 PL/SQL 块中放入任何更改语句,除非它是动态 SQL。它说“在期待以下之一时遇到符号“ALTER”......”。我的目的是有一个程序可以在需要时运行以刷新数据。而且我需要一个 PL/SQL 块来从远程 DB 中获取值——它不能与子查询一起使用来获取“alter sequence myseq increment by X”的值。

标签: oracle plsql sequence dynamic-sql


【解决方案1】:

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

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


根据您提供的信息,这可能就是您所需要的。而如果你想设置序列的当前值,你不能,根据这个documentation,你需要drop/create:

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;

【讨论】:

  • 对不起,我之前不清楚:(我实际上是在尝试改变同一个数据库中的序列。
【解决方案2】:

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

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 内部使用),所以我们不应该使用它。 Still true in 20c. 唯一批准的更改序列值的机制是我上面概述的。

【讨论】:

  • +1 以获得很好的答案。抱歉,我之前不清楚:(我实际上是在尝试更改同一个数据库中的序列。
  • @[APC]谢谢,伙计!它终于奏效了!我希望我能给你+10!
【解决方案3】:

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

AUTHID CURRENT_USER

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

【讨论】:

    【解决方案4】:

    我拿了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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-13
      • 1970-01-01
      • 2018-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多