【问题标题】:insert multiple row into table using select however table has primery key in oracle SQL [duplicate]使用select将多行插入表中,但是表在oracle SQL中具有主键[重复]
【发布时间】:2014-12-26 12:36:20
【问题描述】:

我在一次向表中插入多行时遇到问题,因为列 id 具有主键并且它是基于序列创建的。

例如:

create table test (
  iD number primary key,
  name varchar2(10)
);


insert into test values (123, 'xxx');
insert into test values (124, 'yyy');
insert into test values (125, 'xxx');
insert into test values (126, 'xxx');

以下语句会导致违反约束的错误:

insert into test
(
   select (SELECT MAX (id) + 1 FROM  test) as id,
   name from test
  where name='xxx'
 );

此查询应在表 test 中插入 3 行(name=xxx)。

【问题讨论】:

    标签: oracle


    【解决方案1】:

    您是说您的查询根据序列插入具有主键 ID 的行。然而,在您的插入/选择中有select (SELECT MAX (id) + 1 FROM test) as id,这显然不是 基于序列。可能是您没有以通常的 Oracle 方式使用术语“序列”。

    不管怎样,你有两个选择……

    1. Create a sequence,例如seq_test_id 的起始值为 select max(id) from test,并在查询中使用它(即 seq_test_id.nextval)而不是 select max(id)+1 from test
    2. 将实际的子选择修复为 nvl((select max(id) from test),0)+rownum 而不是 (select max(id)+1 from test)

    请注意,但是,当您的代码在多个并发数据库会话中运行时,选项 2(以及您的原始解决方案)会给您带来巨大的麻烦。因此,强烈推荐选项 1。

    【讨论】:

    • +rownum 为我工作。谢谢!
    【解决方案2】:

    使用

    insert into test (
       select (SELECT MAX (id) FROM  test) + rownum as id,
       name from test
      where name='xxx'
     );
    

    作为一种解决方法。

    当然,您应该使用 sequences 作为整数主键。

    【讨论】:

      【解决方案3】:

      如果要插入由序列生成的 ID/主键值,则应使用序列而不是选择 max(ID)+1。

      通常这是使用表上的触发器来完成的,该触发器对每一行执行。请参阅下面的示例:

      CREATE TABLE "MY_TABLE" 
      (   
        "MY_ID" NUMBER(10,0) CONSTRAINT PK_MY_TABLE PRIMARY KEY , 
        "MY_COLUMN" VARCHAR2(100)
      );
      / 
      CREATE SEQUENCE  "S_MY_TABLE" 
      MINVALUE 1 MAXVALUE 999999999999999999999999999
      INCREMENT BY 1 START WITH 10 NOCACHE  ORDER  NOCYCLE  NOPARTITION ;
      /
       CREATE OR REPLACE TRIGGER "T_MY_TABLE" 
        BEFORE INSERT
        ON
        MY_TABLE
        REFERENCING OLD AS OLDEST NEW AS NEWEST
        FOR EACH ROW
          WHEN (NEWEST.MY_ID IS NULL)
      DECLARE
         IDNOW NUMBER;
        BEGIN
          SELECT  S_MY_TABLE.NEXTVAL INTO IDNOW FROM DUAL;
          :NEWEST.MY_ID := IDNOW;
        END;
      
      /
      ALTER TRIGGER "T_MY_TABLE" ENABLE;
      /
      insert into MY_TABLE (MY_COLUMN) values ('DATA1');
      insert into MY_TABLE (MY_COLUMN) values ('DATA2');
      insert into MY_TABLE (MY_ID, MY_COLUMN) values (S_MY_TABLE.NEXTVAL, 'DATA3');
      insert into MY_TABLE (MY_ID, MY_COLUMN) values (S_MY_TABLE.NEXTVAL, 'DATA4');
      insert into MY_TABLE (MY_COLUMN) values ('DATA5');
      /
      select * from MY_TABLE;
      

      【讨论】:

      • Jakob,基于触发器的序列生成具有大量上下文切换的所有与性能相关的危险。对于每一行,它做了两个不必要的开关,而实际上不需要开关。我不建议这样做。在插入中直接使用序列总是更好。开发人员的懒惰不是借口。
      • 我们在将遗留应用程序从 SQL-Server(带有 IDENTITY 列)移植到 Oracle 时使用了这种方法。在我们当前的环境中,直接使用序列。
      • 有道理,谢谢解释。我想建议对触发器使用when 子句以避免在调用SQL 上下文中已经填充id 时运行它们的代码。没有太大的改进,但是当开发人员不懒惰时,可以为 DB 带来一些缓解。 :-)
      • 感谢您的建议,我按照您的建议编辑了示例代码
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-18
      • 2021-06-16
      • 1970-01-01
      • 1970-01-01
      • 2011-07-26
      相关资源
      最近更新 更多