Oracle Apex - 使用替代触发器更新视图

时间:2012-02-09 15:38:17

标签: oracle triggers views oracle-apex

Apex初学者在这里。我在Oracle数据库中有一个表单:

create or replace view vw_awkward_view as
select unique tab1.some_column1,
  tab2.some_column1,
  tab2.some_column2,
  tab2.some_column3
from table_1 tab1,
 table_2 tab2
WHERE ....

我需要'tab1.some_column1'上的'unique'子句,因为它的基础表中有很多条目。我还需要在我的视图中包含'tab1.some_column1',因为如果没有它,其余的数据就没有多大意义。

在Apex中,我想创建一个关于此视图的报告,其中包含一个用于编辑它的表单(仅限更新)。我不需要编辑tab1.some_column1。只有视图中的其他列需要可编辑。我通常可以使用'替代'触发器来实现这一点,但是当视图包含'distinct','unique'或'group by'子句时,这看起来不可能。

如果我尝试更新此视图中的行,则会收到以下错误:

ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.

如何避免此错误?我希望我的'替代'触发器启动并执行更新,我不需要编辑具有'unique'子句的列,所以我认为应该可以这样做。

3 个答案:

答案 0 :(得分:2)

我认为你应该能够删除“独特” 如果tab2.some_column1,tab2.some_column2,tab2.some_column3 不是唯一的,那么你想如何更新它们?
如果它们是唯一的那么整个结果:tab1.some_column1,tab2.some_column1,tab2.some_column2,tab2.some_column3是唯一的。

当您在sql查询中声明“unique”或“distinct”时,它不仅适用于所有列'tab1.some_column1'

答案 1 :(得分:2)

希望我在这里找到你问题的正确方向;)

您的查询可以通过执行以下操作来实现:

select a.some_column1, tab2.some_column1, tab2.some_column2, tab2.some_column3 
  from table_2 tab2 
  join (select distinct some_column1 from table_1) a
    on tab2.column_in_tab1 = a.some_column1

您收到ORA-02014错误的原因是由于自动生成的ApplyMRU进程。此过程将尝试锁定(已更改的行):

begin
   for r in (select ... 
               from vw_awkward_view 
              where <your first defined PK column>= 'value for PK1' 
                for update nowait)
   loop
      null;
   end loop;
end;

这是一个无赖,意味着你将无法使用生成的进程。 您必须编写自己的进程来进行更新

为此,您必须使用apex_application中的F ##数组。 如果这听起来完全不熟悉,请看一下: Custom submit processon using the apex_application arrays

此外,来自Oracle本身的here is a how-to for apex from 2004。它仍然使用了大量的htmldb引用,但它的要点就在那里。

(使用apex_item接口构建表单可能是一个好主意,并且可以控制生成的内容和所需的数组。)

它归结为:遍历包含项目的数组,并使用提交的值在视图上执行UPDATE

当然,您没有这种方式锁定,也没有办法防止不必要的更新。 锁定您可以自己做,例如使用select for update方法。在更新之前,您必须在要更改的表中锁定正确的行。如果锁定失败,那么您的进程将失败。

至于“丢失的更新”故事:在这里你需要检查MD5校验和。校验和是从表单中的可编辑列生成的,并放在html代码中。在提交时,然后将此校验和与来自这些相同列的新生成的校验和进行比较,但是在提交时使用来自数据库的值。如果校验和不同,则表示记录在页面加载和页面提交之间发生了变化。您的进程应该失败,因为记录已被更改,并且您不希望将这些记录覆盖。 (如果你采用apex_item方式,那么不要忘记包含MD5_CHECKSUM call(或MD5_HIDDEN)。

重要说明:使用apex_item生成的校验和或仅使用标准表单功能构建要散列的字符串。正如您在apex_item.md5_hidden中看到的,校验和是使用DBMS_OBFUSCATION_TOOLKIT.MD5生成的。 您可以通过两种方式获取数据库中值的校验和:wwv_flow_item.md5或使用dbms_obfuscation。 但是,文档没有提到的是:OTN Apex discussion on MD5 checksums。管道被添加到生成的校验和中!不要忘记这一点,否则它会在你的脸上爆炸,你会在几天之内想知道它到底出了什么问题。

示例:

select utl_raw.cast_to_raw(dbms_obfuscation_toolkit.md5(input_string=>
         "COLUMN1"    ||'|'||
         "COLUMN2"    ||'|'||
         "COLUMN5"    ||'|'||
         "COLUMN7"    ||'|'||
         "COLUMN10"   ||'|'||
         "COLUMN12"   ||'|'||
         "COLUMN14"   ||
         '|||||||||||||||||||||||||||||||||||||||||||'
      )) md5
from some_table

获取some_table表格行的校验和,其中第1,2,5,7,10,12,1列是可编辑的!

最后,这应该是如何构建的:

  1. 循环数组
  2. 为可编辑列的当前值生成校验和 来自数据库
  3. 将此校验和与提交的校验和进行比较 (如果生成了apex_application.g_fcs),如果校验和匹配, 继续更新。如果没有,请在此处失败。
  4. 锁定正确的记录以进行更新。指定nowait,它 锁定失败,失败过程
  5. 使用提交的值更新您的视图。你的替代触发器 会发射。请确保为更新语句使用正确的值,以便只更新这一条记录
  6. 不要在中间提交。这是全部或全部。

    我几乎觉得自己过火了,可能觉得它有点多了,但是当你知道陷阱时,实际上并不是很难将这个自定义过程拉下来!对我来说,这是非常了解的:p

答案 2 :(得分:0)

汤姆的答案是处理问题的正确方法,但如果我理解正确的话,我认为你的要求有些过分。

最简单的方法可能是在要编辑的表格上创建表单。然后让报告编辑链接将用户带到此表单,该表单只会更新一个表中所需的列。如果您需要显示其他表中列的值,则在创建链接以将此值传递给可以包含仅显示项目的表单时,这很简单。