DML 子查询中的 NESTED 表

Posted

技术标签:

【中文标题】DML 子查询中的 NESTED 表【英文标题】:NESTED table in DML subquery 【发布时间】:2020-04-28 10:41:41 【问题描述】:

使用 Oracle 12c EE,如何在 DML 子查询中使用 PL/SQL 包类型而不引发异常“ORA-00902:无效数据类型”?

示例架构

--PL/SQL package types.
create or replace package test_pkg as
    TYPE type_record IS RECORD(
        column1        NUMBER,
        column2        NUMBER,
        column3        NUMBER);

    TYPE type_table IS TABLE OF type_record;
end;
/

--For comparison, the same types but as SQL objects.
CREATE OR REPLACE TYPE type_record IS OBJECT(
    column1        NUMBER,
    column2        NUMBER,
    column3        NUMBER);

CREATE OR REPLACE TYPE type_table IS TABLE OF type_record;

--Table for testing DML.
create table tableX(a number);

SQL SELECT 中的 PL/SQL 类型 - WORKS

从 PL/SQL 类型到 SQL 的转换适用于 SELECTS。下面的代码运行良好:

declare
    vt test_pkg.type_table;
    v_count number;
begin
    select count(*)
    into v_count
    from dual
    where not exists(select column1 from table(vt));
end;
/

SQL 更新中的 PL/SQL 类型 - 失败

但是在 DML 语句中使用相同的类型和子查询会引发异常:“ORA-00902: invalid datatype/ORA-06512: at line 4”。

declare
    vt test_pkg.type_table;
begin
    update tableX set a = 1
    where not exists (select column1 from table(vt));
end;
/

SQL UPDATE 中的 SQL 类型 - WORKS

作为比较,在子查询中使用 SQL 对象在 DML 中效果很好:

declare
    vt type_table;
begin
    update tableX set a = 1
    where not exists (select column1 from table(vt));
end;
/

为每个查询创建 SQL 对象是一种解决方法,但这会创建很多不必要的架构对象。有没有办法让 PL/SQL 包类型在 DML 子查询中工作?

【问题讨论】:

" 我需要停止使用 db 中定义的类型" 为什么?不使用 SQL 类型的驱动因素是什么? 公司要求。保持代码干净。 【参考方案1】:

我想这是一个意见问题,因为我使用 db 级别的类型,因为(恕我直言)这是保持代码干净的原因。但这只是看待它的另一种方式。但是,至少在这种情况下,您要寻找的东西并不难。您已经在创建一个使用这些定义的包。您可以将定义移动到包规范中。然后将需要很少或不需要代码更改。此外,这些定义仍然可以在包本身之外使用。

create or replace package pkg_t1 as  
   type type_record is record(
      column1        number,
      column2        number,
      column3        number);

   type type_table is table of type_record;

   function build_type_records(rows_to_build in integer) 
     return type_table;

   function build_pipe_records(rows_to_build in integer) 
     return type_table 
     pipelined;    

end pkg_t1;
/

create or replace package body pkg_t1 as

   function build_type_records(rows_to_build in integer) 
   return  type_table 
   is  
      v_type type_table := type_table();
   begin 
      for i in 1 .. rows_to_build 
      loop 
         v_type.extend;      
         v_type(i).column1 := trunc(dbms_random.value(1,100)); 
         v_type(i).column2 := trunc(dbms_random.value(100,500));
         v_type(i).column3 := dbms_random.value();
      end loop ;

     return  v_type;
   end build_type_records; 

  function build_pipe_records(rows_to_build in integer) 
    return type_table 
    pipelined 
  is
    v_rec type_table;
  begin
    v_rec:= build_type_records(rows_to_build); 
    for i in 1 .. v_rec.count
    loop
      pipe row (v_rec(i));
    end loop; 
  end build_pipe_records;

end pkg_t1;
/ 

declare 
  tt pkg_t1.type_table; 
  num_of_rows integer := &Number_of_rows;
begin
  tt := pkg_t1.build_type_records( num_of_rows );
  for i in 1 .. tt.count
  loop
      dbms_output.put_line( 'Result: '
                            || 'Column1==>' || tt(i).column1 || ', ' 
                            || 'Column2==>' || tt(i).column2 || ', '
                            || 'Column3==>' || tt(i).column3
                          ) ; 
  end loop; 
end ; 
/

select * from table(pkg_t1.build_pipe_records(&Rows_Desirded));

【讨论】:

我不认为这直接回答了这个问题。 (这些想法可能对原始发帖人有所帮助,但我认为它们并没有解决问题根源的奇怪行为。) 就目前的问题而言,我同意。但现在已经不是几天前的样子了。奇怪的行为在当时甚至现在都不是问题。

以上是关于DML 子查询中的 NESTED 表的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 子查询 派生表子查询错误

连接两个表子查询

MySQL------ 子查询

MySQL------ 子查询

Laravel 多表子查询

MySQL随记 - 子查询