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_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>
我们可以通过 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,比如.
这是测试 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_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>
我们可以通过 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,比如