【问题标题】: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 中的索引”并不自动意味着“自动生成的值”,这就是我认为您所指的。当有很多并发用户时,“最后一个索引”是不可靠的。

标签: sql database


【解决方案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,请告诉我。

                    【讨论】:

                      猜你喜欢
                      • 2014-10-17
                      • 1970-01-01
                      • 2015-05-10
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-10-13
                      • 2011-09-27
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多