如何使用简单的更新语句更新表中的varray类型?

时间:2014-07-30 20:23:04

标签: sql oracle plsql

我有一个表,其列定义为已定义类型的varray。生产表比以下示例更复杂。

我能够选择varray类型中的单个列。但我想用一个简单的更新语句更新表(而不是通过pl / sql例程)。

如果这是不可能的(我必须通过pl / sql例程)什么是一种智能而简单的代码编码方法?

update (select l.id, t.* from my_object_table l, table(l.object_list) t) 
set value2 = 'obj 4 upd' 
where value1 = 10 

ORA-01733: virtual column not allowed here

这里是类型的完整示例等。

create or replace type my_object 
as object(
      value1                    number,
      value2                    varchar2(10),
      value3                    number);

create or replace type my_object_varray as varray(100000000) of my_object;

create table my_object_table (id number not null, object_list my_object_varray);

insert into my_object_table 
   values (1, my_object_varray (
                    my_object(1,'object 1',10), 
                    my_object(2,'object 2',20),
                    my_object(3,'object 3',30) 
                 )
          );

insert into my_object_table 
   values (2, my_object_varray (
                    my_object(10,'object 4',10), 
                    my_object(20,'object 5',20),
                    my_object(30,'object 6',30) 
                 )
          );

select l.id, t.* from my_object_table l, table(l.object_list) t;

Type created.
Type created.
Table created.
1 row created.
1 row created.

        ID     VALUE1 VALUE2         VALUE3
---------- ---------- ---------- ----------
         1          1 object 1           10
         1          2 object 2           20
         1          3 object 3           30
         2         10 object 4           10
         2         20 object 5           20
         2         30 object 6           30

6 rows selected.

3 个答案:

答案 0 :(得分:2)

正如Oracle文档所述here

  

虽然嵌套表也可以分段方式更改,   varrays不能。

无法以分段方式修改VARRAYS。你唯一能做的就是:

  • 将您的fied的数据类型转换为NESTED TABLE(创建类型xxx,如表yyy)
  • 获取要更改的行的varray,使用客户端语言进行修改,然后更新行以在其上设置修改后的值。

答案 1 :(得分:2)

我不相信您可以从普通SQL中更新varray内的单个对象的值,因为无法引用varray索引。 (Alessandro Rossi发布的链接似乎支持这一点,但不一定是因为这个原因)。当然,我有兴趣被证明是错的。

我知道你并不热衷于PL / SQL方法,但如果你必须这样做,你可以这样做只是更新那个值:

declare
  l_object_list my_object_varray;
  cursor c is
    select l.id, l.object_list, t.*
    from my_object_table l,
    table(l.object_list) t
    where t.value1 = 10
    for update of l.object_list;
begin
  for r in c loop
    l_object_list := r.object_list;
    for i in 1..l_object_list.count loop
      if l_object_list(i).value1 = 10 then
        l_object_list(i).value2 := 'obj 4 upd';
      end if;
    end loop;

    update my_object_table
    set object_list = l_object_list
    where current of c;
  end loop;
end;
/

anonymous block completed

select l.id, t.* from my_object_table l, table(l.object_list) t;

        ID     VALUE1 VALUE2         VALUE3
---------- ---------- ---------- ----------
         1          1 object 1           10 
         1          2 object 2           20 
         1          3 object 3           30 
         2         10 obj 4 upd          10 
         2         20 object 5           20 
         2         30 object 6           30 

SQL Fiddle

如果您正在更新其他内容,那么您可能更喜欢返回对象列表且更新了相关值的函数:

create or replace function get_updated_varray(p_object_list my_object_varray,
  p_value1 number, p_new_value2 varchar2)
return my_object_varray as
  l_object_list my_object_varray;
begin
  l_object_list := p_object_list;
  for i in 1..l_object_list.count loop
    if l_object_list(i).value1 = p_value1 then
      l_object_list(i).value2 := p_new_value2;
    end if;
  end loop;

  return l_object_list;
end;
/

然后将其称为更新的一部分;但您仍然无法直接更新内嵌视图:

update (
  select l.id, l.object_list
  from my_object_table l, table(l.object_list) t
  where t.value1 = 10
)
set object_list = get_updated_varray(object_list, 10, 'obj 4 upd');

SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table

您需要根据相关ID进行更新:

update my_object_table
set object_list = get_updated_varray(object_list, 10, 'obj 4 upd')
where id in (
  select l.id
  from my_object_table l, table(l.object_list) t
  where t.value1 = 10
);

1 rows updated.

select l.id, t.* from my_object_table l, table(l.object_list) t;

        ID     VALUE1 VALUE2         VALUE3
---------- ---------- ---------- ----------
         1          1 object 1           10 
         1          2 object 2           20 
         1          3 object 3           30 
         2         10 obj 4 upd          10 
         2         20 object 5           20 
         2         30 object 6           30 

SQL Fiddle

如果您想进一步隐藏复杂性,可以创建一个带有调用该函数的触发器的视图:

create view my_object_view as
  select l.id, t.* from my_object_table l, table(l.object_list) t
/

create or replace trigger my_object_view_trigger
instead of update on my_object_view
begin
  update my_object_table
  set object_list = get_updated_varray(object_list, :old.value1, :new.value2)
  where id = :old.id;
end;
/

然后,更新几乎是你想要的,至少表面上看:

update my_object_view
set value2 = 'obj 4 upd'
where value1 = 10;

1 rows updated.

select * from my_object_view;

        ID     VALUE1 VALUE2         VALUE3
---------- ---------- ---------- ----------
         1          1 object 1           10 
         1          2 object 2           20 
         1          3 object 3           30 
         2         10 obj 4 upd          10 
         2         20 object 5           20 
         2         30 object 6           30 

SQL Fiddle

答案 2 :(得分:0)

你试过这个吗?

UPDATE (
    SELECT value2
    FROM
        TABLE(SELECT object_list FROM my_object_table)
    WHERE value1 = 10
) t
SET t.value2 = 'object 4 upd';

我无法测试此查询,请小心使用。我不确定甲骨文是否能真正做到这一点......