制作双插的最佳方法

时间:2020-03-06 14:54:32  来源:igfitidea点击:

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

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

我在各种DBMS postgreSQL,Informix,MySQL和MSSQL中遇到了这个问题(感谢lomaxx的回答)

解决方案

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

MSDN上的这篇文章提供了一个不错的示例。

这是序列解决方案(对于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,如果我们使用同一连接进行多个插入,则事务不要自己动手,这一点很重要。

For LAST_INSERT_ID(), the most
  recently generated ID is maintained in
  the server on a per-connection basis.
  It is not changed by another client.
  It is not even changed if you update
  another AUTO_INCREMENT column with a
  non-magic value (that is, a value that
  is not NULL and not 0). Using
  LAST_INSERT_ID() and AUTO_INCREMENT
  columns simultaneously from multiple
  clients is perfectly valid. Each
  client will receive the last inserted
  ID for the last statement that client
  executed.
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)

另一种选择是创建一个序列,然后在将其插入表中之前将序列值存储在变量中,并使用该值插入两个表中。

对于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字节整数)。这些接口的详细接口略有不同,但是基本原理是相同的。

如果表是UUID键,请生成UUID并在两个插入中都使用它。

Microsoft知识库中介绍了Access 2000+(Jet 4.0)的答案。基本上,我们可以使用SELECT @@ Identity来获取在连接上生成的自动增量字段的值。

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

可以省略" 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)
;

如果我们使用的是SQL Server 2005+,则还可以使用OUTPUT子句,该子句输出已更新,插入或者删除的数据。它非常酷,而且完全适合我们需要的东西类型。
http://msdn.microsoft.com/en-us/library/ms177564.aspx

在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值存储在变量中,以免该值被以后的维护代码破坏。

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

在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 / Data操作层。

如果它在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);

这是链接

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

使用事务来避免此问题:"这可能不是很有用,因为最后的索引在插入之间可能会发生变化,因为另一个用户可以在表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代码段。