制作双插入的最佳方法

Posted

技术标签:

【中文标题】制作双插入的最佳方法【英文标题】:Best way to make double insert 【发布时间】:2008-09-30 05:51:40 【问题描述】:

在表 A 中插入信息并使用表 A 中的索引与表 B 相关联的最佳方法是什么。

我尝试的“解决方案”是将信息插入表 A(具有自动生成的 ID)中,然后选择最后一个索引并将其插入表 B。这可能不是很有用,因为最后一个索引可能在插入之间更改,因为另一个用户可以在表 A 中生成新索引

我在使用各种 DBMS postgreSQL、Informix、mysql 和 MSSQL 时遇到过这个问题(感谢 lomaxx 的回答)

【问题讨论】:

我认为您的问题可能更清楚。 “表 A 中的索引”并不自动意味着“自动生成的值”,这就是我认为您所指的。当有很多并发用户时,“最后一个索引”是不可靠的。 【参考方案1】:

如果您使用的是 MSSQL,则可以使用 SCOPE_IDENTITY 返回当前会话中插入的最后一个 id。然后,您可以使用它来插入表 B。

This article from MSDN 给出了一个很好的例子来说明如何做到这一点。

【讨论】:

【参考方案2】:

这是顺序解决方案(对于 postgres),当然,您必须在存储过程或应用程序代码中执行此操作。

postgres=# create table foo(id serial primary key, text varchar);
NOTICE:  CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
CREATE TABLE

postgres=# create table bar(id int references foo, text varchar);
CREATE TABLE
postgres=# select nextval('foo_id_seq');
 nextval
---------
       1
(1 row)

postgres=# insert into foo values (1,'a'); insert into bar values(1,'b');
INSERT 0 1
INSERT 0 1

对于MySQL,交易很重要,不要自己绊倒,以防您将同一连接用于多个插入。

对于 LAST_INSERT_ID(),最 最近生成的 ID 保存在 服务器基于每个连接。 它不会被其他客户端更改。 如果你更新它甚至不会改变 另一个 AUTO_INCREMENT 列 非魔法值(即,一个值 不是 NULL 也不是 0)。使用 LAST_INSERT_ID() 和 AUTO_INCREMENT 同时从多个列 客户是完全有效的。每个 客户端将收到最后插入的 客户端最后一条语句的 ID 执行。

mysql> create table foo(id int primary key auto_increment, text varchar(10)) Engine=InnoDB;
Query OK, 0 rows affected (0.06 sec)

mysql> create table bar(id int references foo, text varchar(10)) Engine=InnoDB;
Query OK, 0 rows affected (0.01 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into foo(text) values ('x');
Query OK, 1 row affected (0.00 sec)

mysql> insert into bar values (last_insert_id(),'y');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

【讨论】:

【参考方案3】:

另一种选择是创建一个序列,然后在插入表之前获取变量中的序列值并使用它来插入两个表。

【讨论】:

这不会导致同样的问题吗? 可能无法完全理解您的问题。如果我们将值分配给局部变量,我们就会知道该值。不确定这 2 个插件是在同一个地方,还是分别在同一个地方。如果您在表 A 中进行批量插入,然后在表 B 中进行插入,则处理起来会更加棘手。【参考方案4】:

在ORACLE中,使用序列来保持PK值,并使用RETURNING子句

INSERT INTO table1 ( pk_table1, value1 ) 
   VALUES ( table1_seq.NEXTVAL, p_value1 ) RETURNING pk_table1 INTO l_table1_id;

INSERT INTO table2 ( pk_table2, pk_table1, value2 ) 
  VALUES ( table2_seq.NEXTVAL, l_table1_id, p_value2 );

最好在 Oracle 中使用 PACKAGES 来存储应用程序的所有 SQL / 数据操作层。

【讨论】:

【参考方案5】:

对于 IBM Informix Dynamic Server (IDS),它取决于您用于实现双插入的语言。如果它是服务器(SPL - 存储过程语言),并且如果您使用的是 SERIAL 列,那么您使用 DBINFO('sqlca.sqlerrd2') 来表示插入表 B 时添加到表 A 的序列值。如果您在客户端(ESQL/C、I4GL、JDBC、ODBC)中工作,您通过批准的接口(ESQL/C 中的 sqlca.sqlerrd[1],I4GL 中的 sqlca.sqlerrd[2])收集串行,然后传输它又回来了。

IDS 也支持序列,因此您可以使用该技术。

IDS 11.50 支持 SERIAL8 和 BIGSERIAL 以及 SERIAL(一个 4 字节整数);每一个的详细接口略有不同,但基本原理是相同的。

【讨论】:

我使用的是JDBC,如何采集序列号?【参考方案6】:

如果您的表是 UUID 键控的,请生成 UUID 并在两次插入中使用它。

【讨论】:

【参考方案7】:

Microsoft Knowledge Base 中描述了 Access 2000+ (Jet 4.0) 的答案。基本上,您可以使用SELECT @@Identity 来检索在您的连接上生成的自动增量字段的值。

【讨论】:

【参考方案8】:

另一个 Access 2000+ (Jet 4.0) 的答案是创建一个 Jet 4.0 VIEW(在 Access 术语中:将 SELECT 查询保存为查询对象)在 INNER JOINIDENTITY(自动编号)柱子;连接列必须在 SELECT 子句和引用的表中公开。然后INSERT INTO VIEW 为所有没有DEFAULTNOT NULL 列提供值。

IDENTITY 列值可以被省略,在这种情况下,引擎将像往常一样自动生成该值,或者提供并接受一个明确的值;如果另外提供了另一个表中连接列的值(没有IDENTITY 列的那个),那么它必须与IDENTITY 值相同,否则会发生错误;如果IDENTITY 值被省略,那么为连接列提供的任何值都将被忽略。请注意,在此类表之间通常会出现FOREIGN KEY,但这不是此过程正常工作的先决条件。

快速示例(ANSI-92 查询模式 Jet 4.0 语法):

CREATE TABLE Table1 
(
   key_col INTEGER IDENTITY NOT NULL PRIMARY KEY, 
   data_col_1 INTEGER NOT NULL
)
;
CREATE TABLE Table2
(
   key_col INTEGER NOT NULL, 
   data_col_2 INTEGER NOT NULL, 
   PRIMARY KEY (key_col, data_col_2)
)
;
CREATE VIEW View1
AS 
SELECT T1.key_col AS key_col_1, T2.key_col AS key_col_2, 
       T1.data_col_1, T2.data_col_2
  FROM Table2 AS T2
       INNER JOIN Table1 AS T1
          ON T1.key_col = T2.key_col
;
INSERT INTO View1 (data_col_1, data_col_2) 
VALUES (1, 2)
;

【讨论】:

【参考方案9】:

如果您使用的是 sql server 2005+,您还可以使用 OUTPUT 子句输出已更新、插入或删除的数据。它非常酷,完全适合您需要的东西。 http://msdn.microsoft.com/en-us/library/ms177564.aspx

【讨论】:

【参考方案10】:

在 SQL Server 中,您使用@@IDENTITY 字段,并将INSERT 包装在事务中。

DEFINE ... etc etc 

BEGIN TRANSACTION

INSERT INTO table1 ( value1 ) VALUES ( @p_value1 )
SET @pk_table1 = @@IDENTITY

INSERT INTO table2 ( pk_table1, value2 ) VALUES ( @pk_table1, @p_value2 )

COMMIT

TSQL 中的最佳做法是在 INSERT 之后立即将 @@IDENTITY 值存储在变量中,以避免该值被未来的维护代码破坏。

使用存储过程也是最佳实践。

【讨论】:

【参考方案11】:

如果在Informix和JSP中,有一个函数在插入后返回表的Serial字段。

import com.informix.jdbc.*;

cmd = "insert into serialTable(i) values (100)";
stmt.executeUpdate(cmd);
System.out.println(cmd+"...okay");
int serialValue = ((IfmxStatement)stmt).getSerial();
System.out.println("serial value: " + serialValue);

Here's the Link

(出于某种原因,在我的工作计算机中,它用西班牙语描述了所有内容,可能是因为在墨西哥)

【讨论】:

【参考方案12】:

使用事务来避免这个问题:“这可能不是很有用,因为最后一个索引可能会在插入之间发生变化,因为另一个用户可以在表 A 中生成新索引。”

而且,在 PostgreSQL 中,你可以使用 'nextval' 和 'currval' 来完成你想做的事情:

BEGIN;

INSERT INTO products (prod_id, prod_name, description) VALUES (
    nextval('products_prod_id_seq')
    , 'a product'
    , 'a product description'
);

INSERT INTO prices (price_id, prod_id, price) VALUES (
    nextval('prices_price_id_seq')
    , currval('products_prod_id_seq')
    , 0.99
);

COMMIT;

如果您也需要 DDL sn-p,请告诉我。

【讨论】:

以上是关于制作双插入的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

使用相同指数打印 2 个双打的最佳方法

在 PHP 中将十进制/双精度/浮点值与 PDO 绑定的最佳方法是啥?

为文本编辑控件实现缓冲区的最佳方法是啥?

Perl 最佳实践(节选) --- 04

从使用 MySQL _FETCH 设置的字符串中删除双引号的最佳方法是啥?

制作 DAL 的最佳方法是啥?