1

这是尝试使用增加列中值的函数来测试 VIRTUAL 列的使用。

我正在使用一个函数,该函数将返回当前年份的最后两位数字,并用连字符连接,后跟表列中定义为虚拟列的下一个最大值。

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

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;
4

1 回答 1

2

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

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

但更糟糕的是,您的函数正在操作虚拟列。这类似于查询其所属表的触发器上的变异表错误。

“这无疑是一个了解虚拟列用途的测试用例”

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

您问题的另一部分与使用固定分组(例如年份)递增序列号有关。如果您需要这样做,您可以实现一个代码控制表,例如我在对其他 SO 问题的回答中显示的

于 2017-06-04T08:16:02.823 回答