如何在dup_val_on_index Oracle SQL上处理自动递增序列

Bud*_*ddy 1 sql oracle oracle11g

我有一个Oracle表

  1. 使用序列的自动增量主键。
  2. 唯一键
  3. 非唯一字段
create table FOO (
    ai_id number primary key,
    name varchar(20),
    bar varchar(20)
    CONSTRAINT foo_uk_name UNIQUE (name)
);
create sequence  FOO_seq;

create or replace trigger FOO_trg
before insert on FOO
for each row
begin
  select FOO_seq.nextval into :new.ai_id from dual;
end;
Run Code Online (Sandbox Code Playgroud)

我有单独的存储过程,可以对表进行更新

create PROCEDURE UPSERT_FOO(
  name_input IN VARCHAR2,
  bar_input IN VARCHAR2
begin
  begin
    insert into FOO ( name, bar )
    values ( name_input, bar_input )
  exception
    when dup_val_on_index then
      update FOO 
      set  bar = bar_input
      where name = name_input
  end;
end;
Run Code Online (Sandbox Code Playgroud)

This works perfectly fine but the only issue is, sequence "FOO_seq" always increases regardless of whether it is an update or insert(As FOO_seq increments in "FOO_trg" before it inserts).

Is there a way to increment the sequence, only when there is an insert, without hurting the performance?

Ale*_*ole 5

Oracle has a built-in merge statement to do an 'upsert':

create PROCEDURE UPSERT_FOO(
  name_input IN VARCHAR2,
  bar_input IN VARCHAR2
) as
begin
  merge into foo
  using (
    select name_input as name, bar_input as bar from dual
  ) src
  on (foo.name = src.name)
  when matched then
    update set foo.bar = src.bar
  when not matched then
    insert (name, bar)
    values (src.name, src.bar);
end;
/
Run Code Online (Sandbox Code Playgroud)

The insert only happens (and thus the trigger only fires, incrementing the sequence) if there is no match.

That doesn't have to be done through a procedure now, of course; you could just issue a merge directly, plugging in the name/bar values you would currently have to pass to the procedure.

Incidentally, your trigger could be simplified slightly to do an assignment:

create or replace trigger FOO_trg
before insert on FOO
for each row
begin
  :new.ai_id := FOO_seq.nextval;
end;
/
Run Code Online (Sandbox Code Playgroud)

db<>fiddles using your original code and using the code above. Notice the ID for 'b' in the final query; 5 in the first one, but only 2 in the second one.

Gaps in sequences shouldn't matter, of course; they are guaranteed to increment and be unique (if they don't cycle), not to be gapless. Or to necessarily be issued in strict order if you have a cache and are using RAC. Still, your approach would potentially waste a lot of values for no reason, and it doesn't need to be that complicated.