关于oracle11g:Oracle中是否为具有char语义的列支持默认值?

Are default values supported in Oracle for columns with char semantics?

我试图在表中添加一列,但是DEFAULT子句产生了令人惊讶的效果。在具有现有行的表中,我添加了一个新列,如下所示:

1
alter table t add c char(1 char) default 'N' not null;

当我随后向表中添加检查约束时,它失败了:

1
alter table t add constraint chk check(c in ('N', 'Y'));

导致

的原因

1
2
ERROR at line 1:
ORA-02293: cannot validate (T.CHK) - check constraint violated.

其他信息:

  • 因为我要显式设置单位(即char(1 char)而不是char(1)),所以我不希望nls_length_semanatics的值有意义。
  • 在将列添加为char(1 char)后,新添加的" N"实际上是" N",我不确定额外的空格是什么。
  • 按预期将列添加为char(1 byte);
  • 添加没有"默认'N'不为空"的列,然后将所有现有行更新为'N',然后将该列更改为'不为空',也可以按预期进行。
  • NLS_CHARACTERSET是AL32UTF8,但我也不希望这与之相关。
  • 数据库是Oracle 11g; 11.2.0.1.0。
  • 谢谢。


    我相信您看到的是一个依赖于几个不同事物交互的错误

    • 首先,数据库字符集必须是可变宽度的字符集(即AL32UTF8),以便单个字符可能需要最多四个字节的存储空间。
    • 其次,必须使用字符长度语义声明该列
    • 第三,从11.1开始,Oracle进行了优化,以便在表中添加一个声明为NOT NULL且具有DEFAULT的列,Oracle可以通过更新数据字典而不是实际存储默认值来做到这一点。表的每一行中的值。

    当这两种情况都成立时,似乎返回的值的长度为4,并用CHR(0)字符填充。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    SQL> select * from v$version;

    BANNER
    --------------------------------------------------------------------------------
    Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
    PL/SQL Release 11.2.0.1.0 - Production
    CORE    11.2.0.1.0      Production
    TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
    NLSRTL Version 11.2.0.1.0 - Production

    SQL> create table foo( col1 number );

    Table created.

    SQL> insert into foo values( 1 );

    1 row created.

    SQL> commit;

    Commit complete.

    SQL> alter table foo add c char(1 char) default 'N' not null;

    Table altered.

    SQL> alter table foo add constraint chk_foo check( c in ('Y', 'N') );
    alter table foo add constraint chk_foo check( c in ('Y', 'N') )
                                   *
    ERROR at line 1:
    ORA-02293: cannot validate (SCOTT.CHK_FOO) - check constraint violated

    SQL> select c, dump(c) from foo;

    C    DUMP(C)
    ---- ------------------------------
    N    Typ=1 Len=4: 78,0,0,0

    如果您实际上强制将值存储在表中,则会在没有CHR(0)填充的情况下获得预期的行为。因此,如果我在表中插入新行,它将通过。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SQL> insert into foo(col1) values (2);

    1 row created.

    SQL> select c, dump(c) from foo;

    C    DUMP(C)
    ---- ------------------------------
    N    Typ=1 Len=4: 78,0,0,0
    N    Typ=1 Len=1: 78

    您还可以发出UPDATE来更新在表

    的行中未实际存储值的行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SQL> update foo
      2     set c = 'N'
      3   where c != 'N';

    1 row updated.

    SQL> select c, dump(c) from foo;

    C    DUMP(C)
    ---- ------------------------------
    N    Typ=1 Len=1: 78
    N    Typ=1 Len=1: 78


    您已经提到过,导致ORA-02293错误的原因是因为它插入的是\\'N \\'(带有空格),而不是\\'N \\'。因此违反了您的约束。

    更有趣的问题是,为什么要增加该空间?好吧,按照定义,CHAR是固定宽度的,而VARCHAR不是固定宽度。 CHAR将始终填充空白以填充为该列分配的整个内存空间。因为您选择的宽度为1 CHAR,并且AL32UTF8是宽度可变的字符集,所以这似乎与CHAR的固定宽度性质冲突。看起来它被填充以填充\\'N \\'未使用的额外字节。或者,至少,我认为这是正在发生的事情。


    您已标记oracle11g,但未指定版本。

    这适用于Linux x86-64上的11.2.0.2。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    SQL*Plus: Release 11.2.0.2.0 Production on Mon Mar 26 13:13:52 2012

    Copyright (c) 1982, 2010, Oracle.  All rights reserved.

    Connected to:
    Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
    With the Partitioning, Real Application Clusters, Automatic Storage Management and OLAP options

    SQL> create table tt(a number);

    Table created.

    SQL> insert into tt values (1);

    1 row created.

    SQL> commit;

    Commit complete.

    SQL> alter table tt add c char(1 char) default 'N' not null;

    Table altered.

    SQL> alter table tt add constraint chk check(c in('N','Y'));

    Table altered.

    SQL> select * from tt;

         A C
    ---------- -
         1 N

    SQL> column dump(c) format a30
    SQL> select c, length(c),dump(c) from tt;

    C  LENGTH(C) DUMP(C)
    - ---------- ------------------------------
    N      1 Typ=96 Len=1: 78

    那么......您的版本可能有错误吗?

    希望有帮助。