【问题标题】:Virtual Column using deterministic user defined function使用确定性用户定义函数的虚拟列
【发布时间】:2016-08-03 01:57:57
【问题描述】:

我正在使用具有确定性用户定义函数的虚拟列(oracle 11g),该函数将行的主键作为参数并返回一个简单的标量值。虚拟列更新没有任何问题,但是当我更新表时,它会抛出错误:- ora-00054 资源繁忙并在指定 nowait 的情况下获取或在 oracle 中超时。我的表结构和功能如下:-

 -----------------------------------------------------------------------
   id   employee_name    employee_dept  employee_leaves (vir column)
 -----------------------------------------------------------------------
   2     patrick           mgmt         getEmpLeaves(id)  
   3      jack             sales            "
 -----------------------------------------------------------------------

     create or replace function getEmpLeaves(empId number) 
        return number 
          DETERMINISTIC
       is
           emp_leaves number;
       begin
           select leaves into emp_leaves from tbl_emp_leaves 
           where tbl_emp_leaves.id = empId;
           return emp_leaves;
      end ;
    -------------------------------------------------------------

如何克服这个错误?

【问题讨论】:

  • 您能否显示两个表的表创建语句,以及您为获得该异常而发出的语句?说出您所在的版本(版本 1 或版本 2)和补丁级别可能会有所帮助。我无法立即在 11.2.0.4.7 中复制,但我正在做一些假设。是否也涉及索引?

标签: oracle oracle11g calculated-columns virtual-column


【解决方案1】:

我不会寻找这个错误的原因。
一个简短的回答是:从表中删除这个虚拟列,然后创建一个视图:

create view vw_employees AS
SELECT t.id, t.employee_name, t.employee_dept, x.leaves As employee_leaves
FROM tbl_employees t
JOIN tbl_emp_leaves x
ON t.id = x.id;

长答案:请看下面的简单测试用例:

create table tbl_emp_leaves as
select object_id as id, trunc(dbms_random.value(0,100)) as leaves
from all_objects;
alter table tbl_emp_leaves add primary key( id );

create or replace function getEmpLeaves(empId number) 
        return number 
          DETERMINISTIC
       is
           emp_leaves number;
       begin
           select leaves into emp_leaves from tbl_emp_leaves 
           where tbl_emp_leaves.id = empId;
           return emp_leaves;
      end ;
      /

create table tbl_employees as
select object_id as id, object_name as employee_name, object_type as employee_dept
from all_objects;

alter table tbl_employees 
add employee_leaves as ( getEmpLeaves(id)); 

create view vw_employees AS
SELECT t.id, t.employee_name, t.employee_dept, x.leaves As employee_leaves
FROM tbl_employees t
JOIN tbl_emp_leaves x
ON t.id = x.id;

现在比较两个简单查询的性能:

SQL> set timing on;
SQL> select sum(employee_leaves) from vw_employees;

SUM(EMPLOYEE_LEAVES)
--------------------
             3675425

Elapsed: 00:00:00.07
SQL> select sum(employee_leaves) from tbl_employees;

SUM(EMPLOYEE_LEAVES)
--------------------
             3675425

Elapsed: 00:00:03.09

3.09 秒与 0.07 秒 - 您会看到基于函数的虚拟列比简单连接慢 44 倍(即:4400%)。

【讨论】:

  • 所以我的问题的答案是,虚拟列无法像我使用的那样使用。实际上数据来自 4 个表,并在第 5 个表的一列中累积。我虽然是视图(它会很慢)和物化视图(它不是实时的,需要刷新),并且必须在 4 个表上应用触发器(添加、更新和删除以绘制准确的图片)。虚拟列是最简单和最干净的方法,但在 Oracle 在某些版本中解决此问题之前,我有技术限制。
【解决方案2】:

当我创建一个选择另一个表的函数时,我遇到了类似的问题。

关键字DETERMINISTIC 表示函数总是为给定的输入参数返回相同的值。开发人员有责任确保这一点。

Oracle 允许这样的函数:

create or replace function getNumber(x number) 
    return number DETERMINISTIC is
begin
  return DBMS_RANDOM.RANDOM;
end;

虽然这实际上与 确定性 函数相反。

UPDATE tbl_emp_leaves SET leaves = ... 语句后函数的返回值发生变化。

所以,我不得不同意 kordirko 的回答:删除这个虚拟列并创建一个视图或使用触发器来设置列 employee_leaves 的值。

【讨论】:

    猜你喜欢
    • 2019-06-10
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多