未定义值顺序时 Oracle Insert Into 如何工作?

How does Oracle Insert Into work when order of values is not defined?

我遇到了一些看起来像这样的代码。我知道它将 return 自动生成的 id,但我不明白的是,当我在调用此函数时传递游标数据时,它如何识别要在哪些列中插入哪些值列顺序未定义?

FUNCTION INSERT_ROW(DATA IN OWNER.TABLE%ROWTYPE)
    RETURN OWNER.TABLE.ID%TYPE
IS 
    l_ID OWNER.MY_TABLE.ID%TYPE;
    l_Data OWNER.MY_TABLE%ROWTYPE := DATA;
BEGIN   
    INSERT INTO OWNER.MY_TABLE
    VALUES l_Data
    RETURNING ID INTO l_ID;

我试图查找许多示例,但我只遇到过按如下顺序定义值的示例 插入 my_table (val2, val3, val4) 值 (2, 3, 4) 返回 val1 INTO val1;

您代码中的插入值语句是对带括号的标准插入值子句的 PL/SQL 扩展。这是 12.2 手册中关于此主题的页面:

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/INSERT-statement-extension.html#GUID-D81224C4-06DE-4635-A850-41D29D4A8E1B

OWNER.TABLE%ROWTYPE 数据类型定义了与 table 具有相同列且顺序相同的记录。您只是将数据以该格式传递给函数并将其传递给变量,然后传递给插入语句。

Oracle 中 table 中列的顺序已定义。查看 ALL_TAB_COLUMNS 视图 - 有一个 COLUMN_ID 列定义了 table 中列的顺序。如果 SELECT 中未给出字段列表(即 SELECT * FROM MY_TABLE),MY_TABLE 中的列将按 ALL_TAB_COLUMNS.COLUMN_ID 顺序返回。这也是在 %ROWTYPE 变量中对列进行排序的方式,也是没有指定字段列表的 INSERT 期望对字段进行排序的方式。

RETURNING子句的主要目的是获取派生列的值,该值是在插入过程中产生的。通常这是从序列派生的技术主键,或者从 12c 开始是 IDENTITY 列。

例如:

create table my_table (
     val1 number generated as identity primary key
     , val2 varchar2(16)
     , val3 varchar2(16)
     , val4 date)
/

declare
    id number;
begin
    INSERT INTO my_table (val2, val3, val4) 
    VALUES ('one', 'test', sysdate) 
    RETURNING val1 INTO id;

    dbms_output.put_line('new id = ' || id);
end;
/

这就是您找到的示例在 INSERT 投影中指定列的原因:主键的值是自动生成的,因此我们没有必要在代码中为其赋值。

现在您的函数在其插入语句中使用记录类型。我们不能用 IDENTITY 列来做到这一点。这个变体 ...

declare
    lrec my_table%rowtype;
    id number;
begin
    lrec.val2 := 'two';
    lrec.val3 := 'test again';
    lrec.val4 :=  sysdate;

    INSERT INTO my_table
    VALUES lrec
    RETURNING val1 INTO id;

    dbms_output.put_line('new id = ' || id);
end;
/

... 会投掷

ORA-32795: cannot insert into a generated always identity column

但我们可以使用 %rowtype 与老式序列和触发器组合:

create table my_table (
     val1 number primary key
     , val2 varchar2(16)
     , val3 varchar2(16)
     , val4 date)
/

create sequence my_seq start with 42;

create or replace trigger my_trg
    before insert on my_table for each row
begin
    :new.val1 := my_seq.nextval;
end;
/

declare
    lrec my_table%rowtype;
    id number;
begin
    lrec.val1 := 1;
    lrec.val2 := 'three';
    lrec.val3 := 'test again';
    lrec.val4 :=  sysdate;

    INSERT INTO my_table
    VALUES lrec
    RETURNING val1 INTO id;

    dbms_output.put_line('new id = ' || id);
end;
/

这里是a LiveSQL demo (free Oracle OTN account required, alas)。如果你 运行 它你会看到触发器覆盖分配的值并且 val1 列具有来自序列的值。