2017-06-04 124 views
1

这是一个尝试使用函数来测试VIRTUAL列的使用情况,以增加列中的值。使用Oracle虚拟列来增加值

我正在使用一个函数,它会返回当前年份的最后两位数字,并与连字符连接,然后是来自表格列的下一个最大值,后者被定义为虚拟列。

当我向表中插入记录时,它确实插入成功。然而,当我查询的记录,我提示以下错误:

ORA-00036:递归SQL级别(50)最大数量超过

我的问题是,是否有可能实现到增加值(使用VIRTUAL列自定义增量或这种尝试是微不足道的?

下面的函数首先通过取消注释注释部分和创建表时首先编译第一个SQL块,然后使用第二个SQL块

功能

CREATE OR REPLACE FUNCTION test_func (
    p_empl_id NUMBER, 
    empl_nm  VARCHAR2) 
    RETURN VARCHAR2 
    DETERMINISTIC 
IS 
    return_value VARCHAR2(32); 
BEGIN 
    return_value := NULL; 

-- SELECT TO_CHAR (SYSDATE, 'YY') 
--   || '-' 
--   || LPAD (
--    TO_CHAR (NVL (MAX (TO_NUMBER (SUBSTR (001, 5))), 0) + 1), 
--    5, 
--    '0') into return_value 
--  FROM dual;  

    SELECT TO_CHAR (SYSDATE, 'YY') 
      || '-' 
      || LPAD (
       TO_CHAR (NVL (MAX (TO_NUMBER (SUBSTR (test_col, 5))), 0) + 1), 
       5, 
       '0') 
    INTO return_value 
    FROM test_table 
    WHERE SUBSTR (test_col, 1, 2) = TO_CHAR (SYSDATE, 'YY'); 

    RETURN return_value; 
END; 
/

表结构

CREATE TABLE test_table 
(
    empl_id  NUMBER, 
    empl_nm  VARCHAR2 (50), 
    monthly_sal NUMBER (10, 2), 
    bonus   NUMBER (10, 2), 
    test_col  AS (test_func (empl_id, empl_nm)) VIRTUAL 
); 

插入语句

INSERT INTO test_table (empl_id, 
         empl_nm, 
         monthly_sal, 
         bonus) 
    WITH data 
     AS (SELECT 100 empl_id, 
        'AAA' empl_nm, 
        20000 monthly_sal, 
        3000 bonus 
       FROM DUAL) 
    SELECT * 
    FROM data; 

我曾尝试使用下面的SQL使用序列尝试,然而,序列值也越来越插入每次我执行SQL语句时表

SELECT TO_CHAR (SYSDATE, 'YY') 
     || '-' 
     || '000' 
     || test_virtual_sequence.NEXTVAL 
    FROM DUAL; 
+0

这只是一个测试练习,还是你想在生产代码中使用这个函数?如果后者是真的,那么使用这样一个函数是一个非常糟糕的主意,请解释一下你真正的需求。 ? – krokodilko

+0

@krokodilko无论如何,我不打算在生产中使用它,正如问题中提到的那样,这当然是一个测试用例,可以知道“虚拟”列的用法。 – user75ponic

回答

2

“ORA-00036:(50)递归SQL级别的最大数量超过”

你得到这个错误是因为你的功能不确定性的部分原因。确定性意味着相同的输入将产生相同的输出。但是,这不适用于您的功能:它不会使用任何输入参数。相反,输出是由我已经插入了多少条记录来管理的。

但更糟的是,你的功能是操纵虚拟列。这与查询其拥有的表的触发器上的变异表错误类似。

“这是肯定的测试用例知道虚拟列的用法”

虚拟列是实现一定量的非规范化的,而不用担心同一数据的不同视图的方式。例如,在ORDER_LINE表中,我们可能有ITEM_COSTLINE_QTY的列。但是我们需要一个LINE_TOTAL的专栏(说支持审批业务规则)。在11g之前,我们不得不添加一个真正的列,并且有维护它的负担(可能在触发器或其他程序代码中)。但是,现在我们可以这样定义它:

, line_qty as (item_cost * line_qty) virtual. 

另一个例子是那种在你的案件关键的。这是用户喜欢的智能钥匙,但数据建模者讨厌:有多个组件,在这种情况下,创建记录的年份和序列号。这些应该适当地建模为单独的列,因此可以在SQL中干净地操作组件,而不需要substr()等。另外,我们需要使用检查约束来强制执行智能密钥的格式。

但是,我们的用户喜欢智能钥匙,因为他们多年来一直在使用这些标识符。那么我们怎么能给他们熟悉的钥匙,但是具有适当的数据完整性呢?随着虚拟列:

虚拟列的
SQL> create table t23 (
    2  created date not null 
    3  , serial_no number not null 
    4  , ref_no as (to_char(created, 'YYYY')||'-'||lpad(serial_no, 5, '0')) virtual 
    5); 

Table created. 

SQL> insert into t23 (created, serial_no) values (sysdate, s23.nextval); 

1 row created. 

SQL> insert into t23 (created, serial_no) values (sysdate, s23.nextval); 

1 row created. 

SQL> select * from t23 
    2/

CREATED SERIAL_NO REF_NO 
--------- ---------- ---------- 
04-JUN-17   3 2017-00003 
04-JUN-17   4 2017-00004 

SQL> 

一个好处是,如果我们改变它依赖于一个值,它会自动同步:

SQL> update t23 
    2 set created = add_months(created, -12) 
    3 where serial_no = 3 
    4/

1 row updated. 

SQL> select * from t23 
    2/

CREATED SERIAL_NO REF_NO 
--------- ---------- ---------- 
04-JUN-16   3 2016-00003 
04-JUN-17   4 2017-00004 

SQL> 

这多,我们可以用在表视图实现。但是,虚拟列拥有了一个可以建立在他们的索引和约束利益:

SQL> alter table t23 
    2  add constraint t23_ref_no unique (ref_no) 
    3/

Table altered. 

SQL> insert into t23 (created, serial_no) values (sysdate, s23.nextval) 
    2/

1 row created. 

SQL> insert into t23 (created, serial_no) values (sysdate, s23.currval); 
insert into t23 (created, serial_no) values (sysdate, s23.currval) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (C.T23_REF_NO) violated 


SQL> 

你的问题的另一部分涉及到增加一个序列号以固定的一组(如一年)。如果你需要这样做,你可以实现一个代码控制表,如I show in my answer to this other SO question

+0

很大。我有一个问题,原始表中的主键不是按顺序排列的,这意味着它不遵循数字顺序,有时会跳转顺序。用户希望看到基于记录的增量数字序列。那么有没有办法获得max + 1而不是使用primary_key(serial_no)的'substr'? – user75ponic

+0

在本答案结束时阅读我链接到的主题。 – APC

+0

如果我们需要使用多个表,code_control表可以使用附加列的通用表? – user75ponic