使用Oracle PL/SQL表类型合并

我想使用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:

请帮我解决这个问题. 如果你能建议一个更好的方法,如果有的话,我将不胜感激.

推荐答案

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

更多仅限PL/SQL的数据类型可以跨PL/SQL到SQL接口

从Oracle数据库12c开始,可以将仅具有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;
/
1 rows affected

dbms_output:
number of rows fetched : 3
number of rows fetched : 3
select *
from ps_test02
ID NAME
1 test01
2 test02
3 test03

fiddle

但是,如果不对提取的数据执行任何额外的PL/SQL处理,则最好将所有内容都保持在SQL级别,并且不要使用上下文切换来移动数据.

Sql相关问答推荐

使用group by后我的平均输出不是我想要的

Django将字符串筛选为整数?

如何查找所提供日期范围的所有季度开始日期和结束日期

如何找到一个组合的两个列,这是不是在其他表在ORACLE SQL?

Oracle 23c ROUND,数据类型为DATE

我如何才能在付款人单列中拉出只有9个付款人单的人?

在 PostgreSQL 中生成时间序列查询

每个分组最多 Select 最后 2 个值并并排显示它们

获取主表条目,其中最新的辅助条目是 6 个月前

使用 SQL 将列添加到 Access 数据库时出错

在presto sql中解析带有区域的时间格式

SQL ORACLE - 查找连续天数

标量子查询中的窗口函数不起作用

多行状态下的分组查询判断状态

获取 SQL Server 中每一行的两个-之间的文本

在 SQL 查询中创建滚动日期

如何按日期和位置对最近 3 个报告日期的 SQL 查询结果进行透视?

SQL查询以获取从特定可变日期看到的用户

为什么 Oracle 在一个查询中对同一张表同时执行 TABLE SCAN 和 INDEX UNIQUE SCAN?

如何在 Oracle 中获取此变量的值?