制作双插入的最佳方法
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 JOIN
上 IDENTITY
(自动编号)柱子;连接列必须在 SELECT 子句和引用的表中公开。然后INSERT INTO
VIEW
为所有没有DEFAULT
的NOT 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,请告诉我。
【讨论】:
以上是关于制作双插入的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
在 PHP 中将十进制/双精度/浮点值与 PDO 绑定的最佳方法是啥?