【问题标题】:Whats the standard way of getting the last insert id?获取最后一个插入 ID 的标准方法是什么?
【发布时间】:2011-02-19 03:12:13
【问题描述】:

获取最后插入的 id 的 sql 标准是什么?如果有这样的事情。

mysql: LAST_INSERT_ID()
postgresql: ... 返回 f_id
mssql: SCOPE_IDENTITY()
...这里有更多示例...

我的意思是,所有数据库对此都有不同的实现,这种常见任务没有标准吗?

【问题讨论】:

  • 我有一个.. 如果 ID 列是 int 并且自动递增。最后一行是 b max(ID).. 你是这个意思吗?
  • @william 天真,但有道理:P
  • @william: 在你获取 MAX(id) 之前发生另一个 INSERT 会发生什么?
  • @mu: 我认为插入的行将被检索.. 插入表值(.).. 然后 select * from table where id=max(id).. 你插入的行将 b取回。这是我的假设。
  • MAX(id) 是错误的。 SELECTing 最后插入值的 ID 的标准方法是使用插入所有值的条件查询。因此,如果您执行INSERT INTO table (a,b,c) VALUES (A,B,C),您将获得带有SELECT id FRM table WHERE a = A AND b = B AND c = C 的ID。如果可能存在重复值(例如记录 A、B、C 不是唯一的),这当然不能依赖。

标签: sql mysql sql-server postgresql standards


【解决方案1】:

看到这个答案Retrieve inserted row ID in SQL

简而言之,除了 MAX(ID) 之外,没有跨数据库的方法可以做到这一点 - 但这不是保证结果,并且有很多陷阱,例如

  • 其他插入可以介于最后插入和最大查询之间
  • 不能与高事务表一起使用(max 会发出读锁,rdbms 特定的方法不会从任何表中读取)

与身份/自动编号/自动增量/序列相关的 ANSI 标准首次出现在 SQL:2003 等待所有主要 RDBMS 实施。它很可能类似于 Oracle/PostgreSQL 序列。

SQL:2003 标准对 SQL:1999(也称为 SQL3)的所有部分进行了微小的修改,并正式引入了一些新功能,例如:

- 序列生成器,它允许标准化序列

SQL:2003 的另一个变化是OUTPUT USING CLAUSE,但关于它的信息很少。 Sybase 和 SQL Server 已经用它做了不同的事情,所以目前还不清楚它会如何发展。 SQL Server 将其实现为

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

【讨论】:

    【解决方案2】:

    Oracle 和 PostgreSQL 支持RETURNING 子句,并使用称为序列的对象来提供自动顺序编号。 SQL Server 的下一个版本 denali 设置为支持序列,但我还没有看到 Denali 是否支持 RETURNING 子句的消息。另一种获取当前序列值的方法是:

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

    DB2 支持sequencesRETURNING INTO 子句。

    SELECT MAX(auto_increment_column) ... 不是推荐的做法,因为它不可靠。在 Oracle 中,读取器 (SELECT) 不会被写入器 (INSERT/UPDATE) 阻止,因此无法保证值正确。

    结论

    我不知道ANSI SQL:2003 standard 包括使用序列进行自动编号,但目前没有实现用于检索值的一致方法。

    【讨论】:

      【解决方案3】:

      一种技术可能适用于所有数据库,并且在序列不是简单递增数字的情况下(例如,存在具有高 id 的预先存在的行,因此您不能使用 MAX),是:

      1. 确定下一个 ID 是什么,例如使用nextval 函数或类似函数。
      2. 使用该 ID 插入行。
      3. 使用 id 来满足您的其他需求。

      【讨论】:

      • 我不确定 SQL Server 是否支持nextval 或类似的东西,但我们可以暂时假装它只是服务器产品的一小部分,所以不算。我想知道的是您的方法实际上应该如何工作。这是一个场景:插入一行,然后很快被删除。从那以后没有再插入任何行,但即将添加一个。 MAX(ID) 目前是一些 N。将使用ID = N + 2 插入新行,因为N + 1 是最近插入和删除的行的ID。使用nextval 会正确解决这种情况吗?
      【解决方案4】:

      这更像是对一些 cmets 的澄清,而不是真正的新答案,但它更适合这里。只要客户端处于可序列化事务中,select max(id) 就可以正常工作。在 pgsql 中,你可以自己证明。打开两个 psql 会话,然后运行它,首先在默认读取提交中,然后在可序列化中:

      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
      

      但是,读取已提交:

      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
      

      性能方面的可序列化事务可能会产生负面影响或导致事务失败,必须回滚并再次尝试,等等。

      返回或 currval() 是更好的主意。但是,如果执行它的事务是可序列化的,那么说 max(id) 不能被信任是错误的。

      【讨论】:

      • 您的意思是“只要每个客户端都在可序列化事务中”吗?
      • 唯一需要在可序列化事务中的客户端是执行 max(id) 的客户端。做其他事情的其他客户端可以处于任一模式。
      猜你喜欢
      • 2011-06-30
      • 2021-10-22
      • 2023-03-16
      • 2011-04-30
      • 2011-06-01
      • 2010-09-18
      • 2011-01-01
      • 1970-01-01
      相关资源
      最近更新 更多