【问题标题】:Multiple insert SQL oracle多插入 SQL oracle
【发布时间】:2015-04-15 21:24:04
【问题描述】:

当你有一个标识列时,如何在 Oracle 12c 中使用 SQL 进行多次插入?

INSERT ALL
INTO Table1 (Column2) Values (1)
INTO Table1 (Column2) Values (2)
SELECT * FROM dual;

其中 Table1 将 column1 作为标识,将标识列设置为具有违反主键约束的相同值。

CREATE TABLE Table1 (
  Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
  column2 VARCHAR2(255),
  column3 NUMBER,
  PRIMARY KEY (Table1Id)
);

INSERT ALL
  INTO Table1 (column2, column3) VALUES ('a', '1')
  INTO Table1 (column2, column3) VALUES ('b', '2')
SELECT * FROM dual;

--SQL Error: ORA-00001: unique constraint violated

我做错了什么?

【问题讨论】:

  • 你没有做错什么。这是 Oracle 使用带有 Insert All 语句的内部序列的方式的问题。我已经编辑了我的答案。你可以看演示。
  • 我又添加了几个测试用例。

标签: sql oracle sql-insert identity-column oracle12c


【解决方案1】:

编辑添加了两个测试用例和一个可能的解决方法。

虽然Insert 语句和insert all 语句实际上是相同的常规插入语句。但是在序列方面,它们的工作方式不同。

测试用例 1:标识列

SQL> DROP TABLE table1 PURGE;

Table dropped.

SQL>
SQL> CREATE TABLE Table1 (
  2    Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
  3    column3 NUMBER,
  4    PRIMARY KEY (Table1Id)
  5  );

Table created.

SQL>
SQL> INSERT ALL
  2    INTO Table1 (column3) VALUES ('1')
  3    INTO Table1 (column3) VALUES ('2')
  4  SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.SYS_C0010439) violated


SQL>

让我们看看幕后实际发生了什么 -

SQL> CREATE TABLE Table1 (
  2    Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
  3    column3 NUMBER,
  4    CONSTRAINT A UNIQUE (Table1Id)
  5  );

Table created.

SQL> INSERT ALL
  2    INTO Table1 (column3) VALUES (1)
  3    INTO Table1 (column3) VALUES (2)
  4  SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.A) violated


SQL> SELECT * FROM table1;

no rows selected

SQL> ALTER TABLE table1
  2  DISABLE CONSTRAINT a;

Table altered.

SQL> INSERT ALL
  2    INTO Table1 (column3) VALUES (1)
  3    INTO Table1 (column3) VALUES (2)
  4  SELECT * FROM dual;

2 rows created.

SQL> SELECT * FROM table1;

  TABLE1ID    COLUMN3
---------- ----------
         2          1
         2          2

SQL>

因此,序列进展到nextval,但是在我们第一次执行全部插入时存在唯一约束违规。接下来,我们禁用了唯一性约束,随后的 Insert All 显示序列没有进入 nextval,而是尝试插入重复键

虽然INSERT-INTO-SELECT 语句不会出现此问题。

SQL> INSERT INTO table1(column3) SELECT LEVEL FROM dual CONNECT BY LEVEL <=5;

5 rows created.

SQL>
SQL> SELECT * FROM table1;

  TABLE1ID    COLUMN3
---------- ----------
         2          1
         3          2
         4          3
         5          4
         6          5

SQL>

令人惊讶的是,根据元数据,序列应该自动进行到 nextval,但它不会发生在 Insert All 语句中。

SQL> SELECT COLUMN_NAME,
  2    IDENTITY_COLUMN,
  3    DATA_DEFAULT
  4  FROM user_tab_cols
  5  WHERE table_name   ='TABLE1'
  6  AND IDENTITY_COLUMN='YES';

COLUMN_NAME     IDENTITY_COLUMN DATA_DEFAULT
--------------- --------------- ------------------------------
TABLE1ID        YES             "LALIT"."ISEQ$$_94458".nextval

SQL>

测试用例 2:显式使用序列

无论是使用标识列还是使用显式序列INSERT ALL 的工作方式都相同。

SQL> DROP SEQUENCE s;

Sequence dropped.

SQL>
SQL> CREATE SEQUENCE s;

Sequence created.

SQL>
SQL> DROP TABLE t PURGE;

Table dropped.

SQL>
SQL> CREATE TABLE t (
  2    ID NUMBER,
  3    text VARCHAR2(50),
  4    CONSTRAINT id_pk PRIMARY KEY (ID)
  5  );

Table created.

SQL>
SQL> INSERT ALL
  2    INTO t VALUES (s.nextval, 'a')
  3    INTO t VALUES (s.nextval, 'b')
  4    INTO t VALUES (s.nextval, 'c')
  5    INTO t VALUES (s.nextval, 'd')
  6  SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.ID_PK) violated


SQL>
SQL> SELECT * FROM T;

no rows selected

SQL>
SQL> ALTER TABLE t
  2    DISABLE CONSTRAINT id_pk;

Table altered.

SQL> INSERT ALL
  2    INTO t VALUES (s.nextval, 'a')
  3    INTO t VALUES (s.nextval, 'b')
  4    INTO t VALUES (s.nextval, 'c')
  5    INTO t VALUES (s.nextval, 'd')
  6  SELECT * FROM dual;

4 rows created.

SQL> SELECT * FROM T;

        ID TEXT
---------- ----------------------------------------
         2 a
         2 b
         2 c
         2 d

SQL>

可能的解决方法 - 使用 ROW LEVEL 触发器

SQL> CREATE OR REPLACE TRIGGER t_trg
  2      BEFORE INSERT ON t
  3      FOR EACH ROW
  4      WHEN (new.id IS NULL)
  5      BEGIN
  6        SELECT s.NEXTVAL
  7        INTO   :new.id
  8        FROM   dual;
  9      END;
 10  /

Trigger created.

SQL> truncate table t;

Table truncated.

SQL> INSERT ALL
  2    INTO t (text) VALUES ('a')
  3    INTO t (text) VALUES ('b')
  4    INTO t (text) VALUES ('c')
  5    INTO t (text) VALUES ('d')
  6  SELECT * FROM dual;

4 rows created.

SQL> SELECT * FROM t;

        ID TEXT
---------- -------------------------
         3 a
         4 b
         5 c
         6 d

SQL>

【讨论】:

  • 他的假设是错误的是什么意思?我可以在 12.1.0.2 上重现他的错误。这看起来像是标识列的一个相当大的问题。
  • 是的,正确,当我创建 test 时,我完全错过了唯一约束。
  • 这不仅仅是身份列的问题,而是Insert All 的工作方式。序列仅获取一次。将解释添加到答案中。我看到的唯一解决方法是旧的基于触发器的方法。行级触发器将增加插入的每一行的序列。
  • @LalitKumarB 在回答中有人说“虽然 INSERT-INTO-SELECT 语句不会出现问题。”但是当我尝试该语句时失败了,因为我们不能使用 sequnce.nextval 作为unionof select 禁止使用。
【解决方案2】:

这是使用UNION ALL 方法而不是INSERT ALL 方法的解决方法。由于某种原因,数据必须包装在select * from (...) 中,否则会生成错误ORA-01400: cannot insert NULL into ("JHELLER"."TABLE1"."TABLE1ID")

insert into table1(column2, column3)
select *
from
(
    select 'a', '1' from dual union all
    select 'b', '2' from dual
);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    • 2016-07-23
    • 2020-09-03
    相关资源
    最近更新 更多