Oracle虚拟列自增值的使用

Usage of Oracle virtual column to increment value

这是测试 VIRTUAL 列的使用情况的尝试,该列具有递增列中值的功能。

我正在使用一个函数,该函数将 return 当前年份的最后两位数与一个连字符连接起来,后跟 table 列中的下一个最大值,该列被定义为虚拟列。

当我将记录插入 table 时,它确实插入成功。但是,当我查询记录时,出现以下错误

ORA-00036: maximum number of recursive SQL levels (50) exceeded

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

首先通过取消注释注释部分编译以下函数,在创建 table 时,第一个 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;
/

Table结构

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 使用序列,但是,每次我从 table[=15= 执行 SQL 语句时都会插入序列值]

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

"ORA-00036: maximum number of recursive SQL levels (50) exceeded"

您收到该错误的部分原因是因为您的函数不是确定性的。确定性意味着相同的输入将产生相同的输出。但这对您的函数而言并非如此:它不使用任何输入参数。相反,输出由我已经插入了多少条记录决定。

但更糟糕的是,您的函数正在操纵虚拟列。这类似于查询其拥有的 Table 的触发器上的变异 table 错误。

"this is a certainly a test case to know the usages of virtual column"

虚拟列是一种实现一定数量的非规范化的方法,而不会冒对相同数据的不同视图的风险。例如,在 ORDER_LINE Table 上,我们可能有 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> 

我们可以通过 table 的视图来实现这一点。但是虚拟列的好处是我们可以在它们上面建立索引和约束:

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>

您问题的另一部分涉及使用固定分组(例如年份)递增序列号。如果你需要这样做你可以实现一个代码控制table,比如.