连接虚拟列表达式中的数字引发 ORA-12899: value too large for column

Posted

技术标签:

【中文标题】连接虚拟列表达式中的数字引发 ORA-12899: value too large for column【英文标题】:Concatenating numbers in virtual column expression throws ORA-12899: value too large for column 【发布时间】:2015-04-17 21:37:23 【问题描述】:

虽然我昨天给了这个answer 一个问题,但我建议使用 VIRTUAL COLUMN 来计算值,而不是手动更新它。

我自己做了一个测试,发现虚拟列表达式在连接两个 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>

那么现在发生了什么?表创建好了,为什么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>

【问题讨论】:

你正在连接两个 lexicalisations 的 oracle 数字。鉴于 - 根据文档 - 它们每个可能有多达 38 个有效数字,报告的错误似乎很好(为符号表示添加 1 个额外位置)。 @collapsar,从数字到 varchar2 的隐式转换使表达式结果的每个值的大小为 varchar2(40)。这种连接本身需要 80 个字节。感谢您的评论。 【参考方案1】:

您的人数不受限制。对于单个数字(正数)知道串联长度只能是三个,但虚拟列必须足够大以容纳任何数字 - 所以它看起来允许最多 40 个数字用于隐式格式模型(38 位有效数字、小数分隔符和符号;@collspar 的 lexicalisation)。

话虽如此,限制数字列不会反映在虚拟列长度中 - 使两个列 NUMBER(1,0) 仍然需要 81 个字符的连接。取生成值won't work either 的子字符串,在这种情况下得到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

【讨论】:

以上是关于连接虚拟列表达式中的数字引发 ORA-12899: value too large for column的主要内容,如果未能解决你的问题,请参考以下文章

oracle跨平台数据迁移 expdp/impdp 字符集问题 导致ORA-02374 ORA-12899 ORA-02372

oracle导入失败,超出长度

PL/SQ连接oracle,L 新建表的时候, virtual那一列是啥意思

在计算列中创建索引提高性能

单行函数

修改Oracle字符集