15

Since Oracle 12c we can use IDENTITY fields.

Is there a way to retrieve the last inserted identity (i.e. select @@identity or select LAST_INSERTED_ID() and so on)?

APC
  • 144,005
  • 19
  • 170
  • 281
bubi
  • 6,414
  • 3
  • 28
  • 45
  • The `identity` columns still use a sequence in the background. You should be able to use the usual `sequence.currval` to obtain the last generated value. –  Jan 18 '16 at 15:55
  • The issue is that I have to know the name of the sequence. Actaully is hard to know the name of the table and the name of the schema where I inserted the records (the name of the sequence is related to them). – bubi Jan 18 '16 at 16:37
  • 1
    How can you _not_ know the name of the table you are inserting into? –  Jan 18 '16 at 17:14
  • You can also use the returning clause depending on exactly what you're trying to do - if you want the ID you just inserted that would possibly work for you, without needing to know the sequence name; if you want the last ID inserted by anybody in any session you probably want to query the table. More context would be helpful. – Alex Poole Jan 18 '16 at 19:42
  • 2
    @a_horse_with_no_name I'm working on a library. The user (of the library) just works on tables and when he need to know the id he just ask for the id. – bubi Jan 19 '16 at 08:16

7 Answers7

18

Well. Oracle uses sequences and default values for IDENTITY functionality in 12c. Therefore you need to know about sequences for your question.

First create a test identity table.

CREATE TABLE IDENTITY_TEST_TABLE
(
  ID NUMBER GENERATED ALWAYS AS IDENTITY 
, NAME VARCHAR2(30 BYTE) 
);

First, lets find your sequence name that is created with this identity column. This sequence name is a default value in your table.

Select TABLE_NAME, COLUMN_NAME, DATA_DEFAULT from USER_TAB_COLUMNS
where TABLE_NAME = 'IDENTITY_TEST_TABLE';

for me this value is "ISEQ$$_193606"

insert some values.

INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla');
INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('aydın');

then insert value and find identity.

INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla');
 SELECT "ISEQ$$_193606".currval from dual; 

you should see your identity value. If you want to do in one block use

declare
   s2 number;
 begin
   INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla') returning ID into s2;
   dbms_output.put_line(s2);
 end;

Last ID is my identity column name.

Atilla Ozgur
  • 14,339
  • 3
  • 49
  • 69
  • This is the only correct answer. You just need to ensure that you're evaluating `.currval` in the same session you're inserting in. – Ben Jan 23 '16 at 18:16
  • 1
    Illuminating answer. Until I read this answer I hadn't thought about the lack of a `currval` capability with IDENTITY columns - yet to use them in real life :( . – APC Jun 13 '19 at 17:44
2

Please check

INSERT INTO yourtable (....)
  VALUES (...)
  RETURNING pk_id INTO yourtable;

It will help you to retrieve last inserted row

Jawad Siddiqui
  • 195
  • 1
  • 1
  • 15
1

IDENTITY column uses a SEQUENCE “under the hood” - creating and dropping sequence automatically with the table it uses. Also, you can specify start with and increment parameters using start with 1000 and increment by 2. It's really very convenient to use IDENTITY when you don't want to operate it's values directly.

But if you need to somehow operate sequence directly you should use another option available in Oracle 12c - column default values. Sutch default values could be generated from sequence nextval or currval. To allow you to have a comprehensible sequence name and use it as "identity" without a trigger.

create table my_new_table
(id number default my_new_table_seq.nextval not null)

You will be always able to call: my_new_table_seq.currval.

It is possible to get ID generated from SEQUENCE on insert statement using RETURNING clause.

For example, create a temporary table:

create global temporary table local_identity_storage ("id" number) on commit delete rows

Make some insert saving this value in the temporary table:

CREATE TABLE identity_test_table (
  id_ident          NUMBER GENERATED BY DEFAULT AS IDENTITY,
  same_value VARCHAR2(100)
);

declare
  v_id number(10, 0);
begin  
  INSERT INTO identity_test_table
    (same_value)
  VALUES
    ('Test value')
  RETURNING id_ident INTO v_id;

  insert into local_identity_storage ("id") values (v_id);
  commit;
end;

Now you have "local" inserted id.

select "id" from local_identity_storage
Mikhailov Valentin
  • 1,092
  • 3
  • 16
  • 23
1

It seems that Oracle implemented IDENTITY just to say that they support identities. Everything is still implemented using SEQUENCES and sometimes you need to access the SEQUENCE to make some of the work (i.e. retrieve the latest inserted IDENTITY).

There is not a way to retrieve the IDENTITY similar to MySQL, SQL Server, DB2, and so on, you have to retrieve it using the SEQUENCE.

bubi
  • 6,414
  • 3
  • 28
  • 45
  • 1
    Yes, it is abstraction over SEQUENCE, but it will change the syntax of INSERT clause and this change will also be reflected in performance. Look at this test (inserting in Oracle 12c) using three different techniques (results taken from here[https://oracle-base.com/articles/12c/identity-columns-in-oracle-12cr1]): 1) TRIGGER_IDENTITY : Time=217 hsecs CPU Time=204 hsecs; 2) DIRECT USAGE SEQUENCE: Time=26 hsecs CPU Time=22 hsecs; 3) REAL_IDENTITY : Time=28 hsecs CPU Time=26 hsecs. – Mikhailov Valentin Jan 21 '16 at 03:54
  • It was done to make migrations to Oracle Database easier. No need to manually create sequence and triggers to fire them on inserts - the database does that for you now. I don't speak for Oracle, but I do have a whitepaper on the subject if you're interested in what else we did in 12c to assist with migrations. – thatjeffsmith Jan 24 '16 at 18:03
  • @thatjeffsmith I'm exactly doing a migration but without a behaviour similar to other DBMS the work is hard. – bubi Jan 25 '16 at 09:35
1

As I've written in this blog post, you could fetch all the current identity values of your schema with a single query:

with
  function current_value(p_table_name varchar2) return number is
    v_current number;
  begin
    for rec in (
      select sequence_name
      from user_tab_identity_cols
      where table_name = p_table_name
    )
    loop
      execute immediate 'select ' || rec.sequence_name || '.currval from dual'
      into v_current;
      return v_current;
    end loop;

    return null;
  end;
select *
from (
  select table_name, current_value(table_name) current_value
  from user_tables
)
where current_value is not null
order by table_name;
/
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
0

What is your scope, global or last user inserted? If global just use

SELECT mytable_seq.nextval MyTableID FROM DUAL

https://www.sitepoint.com/community/t/oracle-last-insert-id-question/1402

If specific encapsulate your inserts & query within a transaction.

Mladen Oršolić
  • 1,352
  • 3
  • 23
  • 43
Krish
  • 5,917
  • 2
  • 14
  • 35
  • Thanks for the answer! The scope is the last user. I think that your suggestion works only for MySQL. In Oracle does not work (syntax error) also adding `FROM Dual` clause. – bubi Jan 15 '16 at 13:02
  • That's for global (I could also use current but is still global) – bubi Jan 15 '16 at 17:16
-3

the last insert will be the highest value of the column. so I think that the easiest way to do it is with the max() method. something like this

select max(id) from table_name
yogev levi
  • 369
  • 1
  • 4
  • 21