制作双插的最佳方法
在表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代码段。