对Oracle PL/SQL表类型使用MERGE语句时,是什么原因导致“无效数据类型”错误?

hk8txs48  于 2023-05-28  发布在  Oracle
关注(0)|答案(3)|浏览(126)

使用Oracle PL/SQL表类型的MERGE
我想使用MERGE语句创建/更新记录。但得到错误“无效的数据类型”。
表设置

CREATE TABLE ps_test01 (id Number primary key,
                        name varchar2(30),
                        active_flag VARCHAR2(1));
                        
                        
CREATE TABLE ps_test02 (id Number primary key,
                        name varchar2(30));
                        
                        
insert into ps_test01 (id, name, active_flag) values (1, 'test01', 'Y');
insert into ps_test01 (id, name, active_flag) values (2, 'test02', 'Y');
insert into ps_test01 (id, name, active_flag) values (3, 'test03', 'Y');

insert into ps_test02 (id, name) values (1, 'test001');

记录类型

create or replace package test_pkg as

TYPE test_rec IS RECORD
   (
      id    number,
      name  varchar2(30),
      active_flag   varchar2(1)
   );
   
   TYPE test_tab_type IS TABLE OF test_rec;

end test_pkg;
/

出错的代码

set SERVEROUTPUT on;
declare
    l_test varchar(10);
    
    cursor cur_ps_test01 IS
        select id, name ,active_flag from ps_test01 where active_flag='Y';
        
    --TYPE test_tab_type is TABLE OF cur_ps_test01%ROWTYPE index by pls_integer;
    test_tab test_pkg.test_tab_type;
    test_tab2 test_pkg.test_tab_type;
        
begin
    
    open cur_ps_test01;
    fetch cur_ps_test01 bulk collect into test_tab;
    close cur_ps_test01;
    
    dbms_output.put_line('number of rows fetched : ' || test_tab.count); 
    
    select * bulk collect into test_tab2 from table(test_tab);
    dbms_output.put_line('number of rows fetched : ' || test_tab2.count); 
    
    
    merge into ps_test02 tab2 -- error was reported on this line
        using ( select id,name,active_flag from table(test_tab) ) tab1
        on (tab1.id = tab2.id)
        when matched then
            update set name = tab1.name
        when not matched then
            insert (id, name) values (tab1.id, tab1.name);
    
end;
/

错误

00902. 00000 -  "invalid datatype"
*Cause:    
*Action:

请帮助我解决此问题。如果你能提出一个更好的方法来做这件事,如果有的话,我很感激。

5cg8jx4n

5cg8jx4n1#

Oracle Database 12 c Release 1(12.1)中的更改中列出了对PL/SQL类型使用的限制。

更多纯PL/SQL数据类型可跨PL/SQL转SQL接口

从Oracle Database 12 c开始,可以将仅PL/SQL数据类型的值绑定到匿名块(即SQL语句)、SQL查询和CALL语句中的PL/SQL函数调用以及SQL查询中的TABLE运算符。不过:

  • ...
  • 如果仅PL/SQL数据类型是关联数组,则不能在非查询DML语句(INSERT、UPDATE、DELETE、MERGE)或子查询中使用该数据类型

您可以使用FORALL语句从集合执行upsert:

declare
    l_test varchar(10);
    
    cursor cur_ps_test01 IS
        select id, name ,active_flag from ps_test01 where active_flag='Y';
        
    --TYPE test_tab_type is TABLE OF cur_ps_test01%ROWTYPE index by pls_integer;
    test_tab test_pkg.test_tab_type;
    test_tab2 test_pkg.test_tab_type;
    var number;
begin
    
    open cur_ps_test01;
    fetch cur_ps_test01 bulk collect into test_tab;
    close cur_ps_test01;
    
    dbms_output.put_line('number of rows fetched : ' || test_tab.count); 
    
    select * bulk collect into test_tab2 from table(test_tab);
    dbms_output.put_line('number of rows fetched : ' || test_tab2.count); 

    forall i in test_tab.first..test_tab.last
      merge into ps_test02 tab2 -- error was reported on this line
      using (
        select
          test_tab(i).id as id,
          test_tab(i).name as name,
          test_tab(i).active_flag as active_flag
        from dual
      ) tab1
        on (tab1.id = tab2.id)
      when matched then
        update set name = tab1.name
      when not matched then
        insert (id, name) values (tab1.id, tab1.name);
    
end;
/
select *
from ps_test02

| ID|名称|
| - -----|- -----|
| 一个|测试01|
| 2|测试02|
| 3|测试03|
fiddle
但是,如果您不对获取的数据执行任何额外的PL/SQL处理,那么最好将所有内容都保持在SQL级别,并且不要使用上下文切换来移动数据。

g9icjywg

g9icjywg2#

作为一个框架挑战,PL/SQL和自定义类型是不必要的,你可以在SQL中完成这一切:

merge into ps_test02 tab2
using (
  select id, name ,active_flag from ps_test01 where active_flag='Y'
) tab1
on (tab1.id = tab2.id)
when matched then
  update set name = tab1.name
when not matched then
  insert (id, name) values (tab1.id, tab1.name);

fiddle
我应该能够有多个查询,可以填充test_tab,这将有助于我在ps_test02表中创建/更新记录。
如果要使用多个表,则可以使用UNION ALLUSING子句中的多个表中进行选择。

l0oc07j2

l0oc07j23#

创建SQL数据类型而不是PL/SQL数据类型:

CREATE OR REPLACE TYPE test_rec AS OBJECT
   (
      id    number,
      name  varchar2(30),
      active_flag   varchar2(1)
   );

CREATE OR REPLACE TYPE test_tab_type AS TABLE OF test_rec;

然后在DECLARE部分中删除对包的引用:

test_tab test_tab_type;

现在您可以在SQL语句中使用test_tab

相关问题