插入 Oracle 并检索生成的序列 ID

Posted

技术标签:

【中文标题】插入 Oracle 并检索生成的序列 ID【英文标题】:Inserting into Oracle and retrieving the generated sequence ID 【发布时间】:2011-07-30 08:40:01 【问题描述】:

我有一些针对 SQL Server 的原始 SQL 查询,它们使用 SCOPE_IDENTITY 在一次执行中执行所有 INSERT 后立即检索特定 INSERT 的生成 ID……

INSERT into Batch(
BatchName,
BatchType,
Source,
Area
) Values (
@strBatchName,
@strType,
@strSource,
@intArea
);

SELECT SCOPE_IDENTITY() BatchID;

问题是:

对于 Oracle 数据库,最好的方法是什么?

这可以通过标准 SQL 在 Oracle 上完成,还是我必须切换它以使用存储过程并在存储过程的主体中放置类似的东西?

如果它必须是一个存储过程,那么检索最后生成的序列号的事实上的标准方法是什么,注意考虑到多个线程上可能会有重叠的执行,所以这个机制需要检索正确的生成的 ID,不一定是最后生成的绝对 ID。

如果两个同时执行,则每个都必须从各自的调用中返回正确的生成 ID。请注意,由于调用的多线程性质,我没有使用 SQL Server 的“@@IDENTITY”。

如果可能的话,我宁愿将其保留为原始 SQL,因为这对我来说更容易跨平台管理(包含每个平台的 SQL 块的单个文件,由 DBMS 标识标签分隔)。存储过程对我来说需要更多的工作来管理,但如果这是唯一可行的方法,我可以这样做。

【问题讨论】:

这些是一些很好的答案。现在我开始执行任务了,它看起来像另一个使用“包”的层,将光标设置为“ROWTYPE”并通过包内的过程或函数返回一些数据将将数据作为结果集返回的最后一步,以便调用者可以执行 SELECT,但将需要插入的数据作为参数传递给包函数。对它很陌生,所以希望一切都有意义。 【参考方案1】:

扩展来自@Guru 和@Ronnis 的答案,您可以隐藏序列并使其看起来更像是使用触发器的自动增量,并有一个为您执行插入并返回生成的ID 的过程作为输出参数。

create table batch(batchid number,
    batchname varchar2(30),
    batchtype char(1),
    source char(1),
    intarea number)
/

create sequence batch_seq start with 1
/

create trigger batch_bi
before insert on batch
for each row
begin
    select batch_seq.nextval into :new.batchid from dual;
end;
/

create procedure insert_batch(v_batchname batch.batchname%TYPE,
    v_batchtype batch.batchtype%TYPE,
    v_source batch.source%TYPE,
    v_intarea batch.intarea%TYPE,
    v_batchid out batch.batchid%TYPE)
as
begin
    insert into batch(batchname, batchtype, source, intarea)
    values(v_batchname, v_batchtype, v_source, v_intarea)
    returning batchid into v_batchid;
end;
/

然后您可以调用该过程而不是执行普通插入,例如来自匿名区块:

declare
    l_batchid batch.batchid%TYPE;
begin
    insert_batch(v_batchname => 'Batch 1',
        v_batchtype => 'A',
        v_source => 'Z',
        v_intarea => 1,
        v_batchid => l_batchid);
    dbms_output.put_line('Generated id: ' || l_batchid);

    insert_batch(v_batchname => 'Batch 99',
        v_batchtype => 'B',
        v_source => 'Y',
        v_intarea => 9,
        v_batchid => l_batchid);
    dbms_output.put_line('Generated id: ' || l_batchid);
end;
/

Generated id: 1
Generated id: 2

您可以在没有显式匿名块的情况下进行调用,例如来自 SQL*Plus:

variable l_batchid number;
exec insert_batch('Batch 21', 'C', 'X', 7, :l_batchid);

... 并使用绑定变量:l_batchid 来引用之后生成的值:

print l_batchid;
insert into some_table values(:l_batch_id, ...);

【讨论】:

自 8i 以来,由于在执行较大插入时的性能问题,我一直在避免行级触发器,但我也不得不承认,从那以后我没有尝试过。它在 11g 或 10g 的情况下表现如何? @Ronnis:还没有将它们与大批量插入一起使用,不足以发表评论。我没有意识到任何问题,但我猜在某些情况下上下文切换(必须从双重选择)可能很重要。而且我想如果你总是从程序中插入,那么触发器是毫无意义的,所以这有点过于复杂......【参考方案2】:

Oracle 中没有针对列的自动递增功能。您需要创建一个 SEQUENCE 对象。您可以使用如下顺序:

insert into table(batch_id, ...) values(my_sequence.nextval, ...)

...返回下一个数字。要找出最后创建的序列 nr(在您的会话中),您可以使用:

my_sequence.currval

This site 有几个关于如何使用序列的完整示例。

编辑:在撰写此答案两年后,Oracle 引入了“身份列”。

【讨论】:

如果另一个表在你调用 my_sequence.currval 之前立即插入,序列下一个数字将不同。 currval 仅在您的会话中调用 nextval 后才有效,并且该值基于每个会话保留。如果会话 A 调用 nextval 并得到 ID = 10,会话 B 调用 nextval 1000 次,如果调用 currval,会话 A 仍然会得到 ID = 10。 Oracle 确实具有自动递增功能。例如身份docs.oracle.com/en/database/other-databases/nosql-database/19.1/… @IEnjoyEatingVegetables,您可以添加自己的答案,我会投票赞成。【参考方案3】:

将其作为存储过程执行确实有很多优点。您可以使用语法insert into table_name values returning 获取插入到表中的序列。

喜欢:

declare
some_seq_val  number;
lv_seq        number;
begin
some_seq_val := your_seq.nextval;
insert into your_tab (col1, col2, col3) 
values (some_seq_val, val2, val3) returning some_seq_val into lv_seq;

dbms_output.put_line('The inserted sequence is: '||to_char(lv_seq));
end;
/

或者直接返回some_seq_val。如果您没有使用 SEQUENCE,而是通过某种计算得出序列,则可以有效地使用returning into

【讨论】:

你甚至不需要那么多步骤;没有必要声明some_seq,你可以做insert into your_tab (col1, col2, col3) values (your_seq.next_val, val2, val3) returning col1 into lv_seq;。另外值得注意的是,您可以将返回值直接放入out 参数中。【参考方案4】:

您可以使用以下语句将插入的 Id 获取到类似变量的东西。

INSERT INTO  YOUR_TABLE(ID) VALUES ('10') returning ID into :Inserted_Value;

现在您可以使用以下语句检索值

SELECT :Inserted_Value FROM DUAL;

【讨论】:

我得到:SP2-0552:未声明绑定变量“INSERTED_VALUE”。已插入 0 行。 @尼克。在执行此代码之前,您需要先声明一个名为“Inserted_Value”的变量 那你为什么不也显示声明呢?答案需要完整。【参考方案5】:

您可以使用单个语句来执行此操作 - 假设您从具有输入/输出参数功能的类似 JDBC 的连接器调用它:

insert into batch(batchid, batchname) 
values (batch_seq.nextval, 'new batch')
returning batchid into :l_batchid;

或者,作为 pl-sql 脚本:

variable l_batchid number;

insert into batch(batchid, batchname) 
values (batch_seq.nextval, 'new batch')
returning batchid into :l_batchid;

select :l_batchid from dual;

【讨论】:

以上是关于插入 Oracle 并检索生成的序列 ID的主要内容,如果未能解决你的问题,请参考以下文章

用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题

Oracle 循环规则 4809

使用 Spring Data R2DBC 进行批量插入时检索生成的 ID

oracle 如何获得新插入记录的id

使用 jdbctemplate / jdbc for Hana db 获取序列生成的主键

插入 nhibernate sql server 后无法检索生成的 id