2017-06-04 130 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