开发者

Whats the standard way of getting the last insert id?

What's the sql standard to get the last inserted id? If there is such a thing.

mysql: LAST_INSERT_ID()

postgresql: ... RETURNING f_id

mssql: SCOPE_IDENTITY()

... more examples here ...

I mean, all databases have different impleme开发者_高级运维ntations for that, there isn't a standard for such a common task?


See this answer Retrieve inserted row ID in SQL

In short, there is no cross database way to do this, except MAX(ID) - but that is not a guaranteed result and has many many pitfalls, e.g.

  • other inserts can come between last insert and max query
  • cannot be used with high transaction tables (max will issue a read lock, rdbms-specific methods do not read from any table)

The ANSI standard that relates to identity/autonumber/auto_increment/sequences first appeared in SQL:2003 awaiting implementation by all major RDBMS. It will most likely resemble Oracle/PostgreSQL sequences.

The SQL:2003 standard makes minor modifications to all parts of SQL:1999 (also known as SQL3), and officially introduces a few new features such as:

- the sequence generator, which allows standardized sequences

Another change in SQL:2003 is the OUTPUT USING CLAUSE but there is very little information about it. Sybase and SQL Server have done different things with it, so it is unclear as yet how it will pan out. SQL Server implements it as

INSERT INTO TBL(..)
OUTPUT inserted.identity_col
INTO @sometablevar
VALUES(..)


Oracle and PostgreSQL support the RETURNING clause, and use an object called a sequence to provide automatic sequential numbering. The next version of SQL Server, denali, is set to support sequences, but I haven't seen word if Denali will support the RETURNING clause. Another means of getting the current sequence value is:

Oracle: sequence_name.CURRVAL 
PostgreSQL: CURRVAL('sequence_name')

DB2 supports sequences, and the RETURNING INTO clause.

SELECT MAX(auto_increment_column) ... is not a recommended practice because it's not reliable. In Oracle, readers (SELECT) aren't blocked by writers (INSERT/UPDATE) so the value can't be guaranteed correct.

Conclusion

I wasn't aware that the ANSI SQL:2003 standard includes using sequences for autonumbering, but at this time there's no consistent means implemented for retrieving the value.


The one technique is likely to work with all DBs, and in circumstances where the sequence is not a simple incrementing number (e.g. there are pre-existing rows with high ids, so you can't use MAX), is:

  1. Determine what the next id will be, e.g. using nextval function or similar.
  2. Insert the row using that id.
  3. Use the id for the rest of your needs.


This is more a clarification to a few comments than a real new answer, but it fits better here. select max(id) works fine as long as the client is in a serializable transaction. In pgsql, you can prove it to yourself. Open two psql sessions, and run this, first in the default read committed and then in serializable:

p1: create table test (id serial);
p1 and p2: begin;
p1 and p2: set transaction isolation level serializable;
p1: insert into test values (DEFAULT);
p2: insert into test values (DEFAULT);
p1: select max(id) from test;
 1
p2: select max(id) from test;
 2
p2: commit;
p1: select max(id) from test;
 2

However, with read committed:

p1: create table test (id serial);
p1 and p2: begin;
p1 and p2: set transaction isolation level read committed;
p1: insert into test values (DEFAULT);
p2: insert into test values (DEFAULT);
p1: select max(id) from test;
 1
p2: select max(id) from test;
 2
p2: commit;
p1: select max(id) from test;
 1

Performance wise serializable transactions can have negative impacts or result in transactions that fail and must be rolled back and tried again, etc.

Returning or currval() are much better ideas. However, to say that max(id) just can't be trusted is wrong, if the transaction doing it is serializable.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜