在虚拟列表达式中连接数字会引发 ORA-12899:列值太大
Concatenating numbers in virtual column expression throws ORA-12899: value too large for column
虽然我昨天把这个 给了一个问题,但我建议使用 VIRTUAL COLUMN 计算值而不是手动更新它。
我自己做了一个测试,发现虚拟列表达式在 concatenating 两个 NUMBER 时占用的数据大小存在问题类型列。不过,连接两个字符时没问题。
数据库版本:
SQL> select banner from v$version where rownum = 1;
BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
SQL>
测试用例 1:连接字符串
SQL> CREATE TABLE t(
2 ID varchar2(2),
3 num varchar2(2),
4 text VARCHAR2(10) generated always as (id||'_'||num) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES ('a', 'e');
1 row created.
SQL> INSERT INTO t(ID, num) VALUES ('b', 'f');
1 row created.
SQL> INSERT INTO t(ID, num) VALUES ('c', 'g');
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NU TEXT
-- -- ----------
a e a_e
b f b_f
c g c_g
SQL>
因此,连接两个字符类型的列没有问题。
测试用例 2:连接数字
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
5 );
text VARCHAR2(10) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
*
ERROR at line 4:
ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 81)
不允许?哼!让我们增加尺寸 -
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(81) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM
---------- ----------
TEXT
--------------------------------------------------------------------------------
1 4
1_4
2 5
2_5
3 6
3_6
SQL> set linesize 200
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------------------------------------------------------------------------------------------------
1 4 1_4
2 5 2_5
3 6 3_6
SQL>
那么现在发生了什么? Table 已创建,但为什么 VIRTUAL COLUMN 在 预期数据大小仅为 3 个字节 时占用那么大的大小,但是需要 81 字节。
检查长度,值是正确的,但是,数据量要大得多。例如,我希望长度为 3,因此我将列的大小声明为 10 个字节。但是虚拟列表达式产生的值比那个大得多。
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
5 );
text VARCHAR2(10) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
*
ERROR at line 4:
ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 40)
SQL>
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(81) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------------------------------------------------------------------------------------------------
1 4 3
2 5 3
3 6 3
SQL> clear columns
columns cleared
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ---------------------------------------------------------------------------------
1 4 3
2 5 3
3 6 3
欢迎任何见解。
UDPATE 感谢 Alex Poole。我没有考虑隐式转换,所以我不关心显式 CAST 表达式。所以,下面的作品 -
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (cast(to_char(id)||'_'||to_char(num) as varchar2(3))) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------
1 4 1_4
2 5 2_5
3 6 3_6
SQL>
您的号码不受限制。对于单个数字(正)数字 you 知道连接长度只能是三个,但虚拟列必须足够大以容纳任何数字 - 所以它看起来最多允许 40 位数字对于隐式格式模型(38 位有效数字、小数点分隔符和符号;@collspar 的词法化)。
话虽如此,限制数字列不会反映在虚拟列长度中 - 使两列 NUMBER(1,0)
仍然需要 81 个字符的串联。获取生成值 的子字符串,在本例中得到 ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 40)
。为每个 to_char()
调用提供格式模型,例如FM999
), 会工作但限制下划线任一侧的值而不是直接限制总长度。
如果要限制列大小,可以将其转换为相同的数据类型和大小,更明确:
text VARCHAR2(10) generated always as
(cast(to_char(id)||'_'||to_char(num) as VARCHAR2(10))) VIRTUAL
虽然我昨天把这个
我自己做了一个测试,发现虚拟列表达式在 concatenating 两个 NUMBER 时占用的数据大小存在问题类型列。不过,连接两个字符时没问题。
数据库版本:
SQL> select banner from v$version where rownum = 1;
BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
SQL>
测试用例 1:连接字符串
SQL> CREATE TABLE t(
2 ID varchar2(2),
3 num varchar2(2),
4 text VARCHAR2(10) generated always as (id||'_'||num) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES ('a', 'e');
1 row created.
SQL> INSERT INTO t(ID, num) VALUES ('b', 'f');
1 row created.
SQL> INSERT INTO t(ID, num) VALUES ('c', 'g');
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NU TEXT
-- -- ----------
a e a_e
b f b_f
c g c_g
SQL>
因此,连接两个字符类型的列没有问题。
测试用例 2:连接数字
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
5 );
text VARCHAR2(10) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
*
ERROR at line 4:
ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 81)
不允许?哼!让我们增加尺寸 -
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(81) generated always as (to_char(id)||'_'||to_char(num)) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM
---------- ----------
TEXT
--------------------------------------------------------------------------------
1 4
1_4
2 5
2_5
3 6
3_6
SQL> set linesize 200
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------------------------------------------------------------------------------------------------
1 4 1_4
2 5 2_5
3 6 3_6
SQL>
那么现在发生了什么? Table 已创建,但为什么 VIRTUAL COLUMN 在 预期数据大小仅为 3 个字节 时占用那么大的大小,但是需要 81 字节。
检查长度,值是正确的,但是,数据量要大得多。例如,我希望长度为 3,因此我将列的大小声明为 10 个字节。但是虚拟列表达式产生的值比那个大得多。
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
5 );
text VARCHAR2(10) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
*
ERROR at line 4:
ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 40)
SQL>
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(81) generated always as (length(to_char(id)||'_'||to_char(num))) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------------------------------------------------------------------------------------------------
1 4 3
2 5 3
3 6 3
SQL> clear columns
columns cleared
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ---------------------------------------------------------------------------------
1 4 3
2 5 3
3 6 3
欢迎任何见解。
UDPATE 感谢 Alex Poole。我没有考虑隐式转换,所以我不关心显式 CAST 表达式。所以,下面的作品 -
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t(
2 ID NUMBER,
3 num NUMBER,
4 text VARCHAR2(10) generated always as (cast(to_char(id)||'_'||to_char(num) as varchar2(3))) VIRTUAL
5 );
Table created.
SQL>
SQL> INSERT INTO t(ID, num) VALUES (1, 4);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (2, 5);
1 row created.
SQL> INSERT INTO t(ID, num) VALUES (3, 6);
1 row created.
SQL>
SQL> SELECT * FROM T;
ID NUM TEXT
---------- ---------- ----------
1 4 1_4
2 5 2_5
3 6 3_6
SQL>
您的号码不受限制。对于单个数字(正)数字 you 知道连接长度只能是三个,但虚拟列必须足够大以容纳任何数字 - 所以它看起来最多允许 40 位数字对于隐式格式模型(38 位有效数字、小数点分隔符和符号;@collspar 的词法化)。
话虽如此,限制数字列不会反映在虚拟列长度中 - 使两列 NUMBER(1,0)
仍然需要 81 个字符的串联。获取生成值 ORA-12899: value too large for column "TEXT" (actual: 10, maximum: 40)
。为每个 to_char()
调用提供格式模型,例如FM999
), 会工作但限制下划线任一侧的值而不是直接限制总长度。
如果要限制列大小,可以将其转换为相同的数据类型和大小,更明确:
text VARCHAR2(10) generated always as
(cast(to_char(id)||'_'||to_char(num) as VARCHAR2(10))) VIRTUAL