【问题标题】:Are default values supported in Oracle for columns with char semantics?Oracle 中是否支持具有 char 语义的列的默认值?
【发布时间】:2012-04-10 04:03:10
【问题描述】:

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

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

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

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

导致

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

其他信息:

  1. 因为我明确设置了单位(即 char(1 char) 而不是 char(1)),我不认为 nls_length_semanatics 的值是相关的。
  2. 将列添加为 char(1 char) 后,新添加的“N”实际上是“N”,我不确定额外的空格是什么。
  3. 按预期将列添加为 char(1 字节);
  4. 添加不带“default 'N' not null”的列,然后将所有现有行更新为“N”,然后将该列更改为“not null”,也可以按预期工作。
  5. NLS_CHARACTERSET 是 AL32UTF8,但我认为这也不相关。
  6. 数据库是Oracle 11g; 11.2.0.1.0.

谢谢。

【问题讨论】:

  • 如果只有值是'N'或'Y',为什么不让它(1个字节)?
  • 尝试将您的检查约束重写为CHECK(C IN (TO_NCHAR('N'), TO_NCHAR('Y')))。我不确定检查约束中是否允许函数调用,但至少值得一试。

标签: oracle oracle11g ddl


【解决方案1】:

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

  • 首先,数据库字符集必须是可变宽度字符集(即AL32UTF8),这样单个字符可能需要最多四个字节的存储空间。
  • 第二,列必须用字符长度语义声明
  • 第三,从 11.1 开始,Oracle 添加了一项优化,以便如果您向表中添加一个声明为 NOT NULL 并且具有 DEFAULT 的列,Oracle 可以通过更新数据字典来完成此操作,而不是实际更新将默认值存储在表的每一行中。

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

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) 填充的预期行为。所以如果我在表中插入一个新行,它就会通过。

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 来更新实际上并未将值存储在表行中的行

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

【讨论】:

  • 另见我的例子和上面的评论。这显然是 11.2.0.1.0 中的一个错误,并在 11.2.0.2.0 中修复。
【解决方案2】:

您标记了 oracle11g,但没有指定版本。

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

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

所以....也许您的版本有错误?

希望对您有所帮助。

【讨论】:

  • 有趣。我在 Windows 和 Linux 上都试过了,Oracle 11.2.0.1.0。所以也许这是一个小版本错误。我会尝试更新。谢谢。
  • @Mark J. Bobak - 你的数据库字符集是什么?我敢打赌,这只发生在具有可变宽度字符集的数据库中(即AL32UTF8)。
  • @JustinCave FTW!我同意,我认为这是可变宽度字符集。 Oracle 必须分配可变宽度字符集允许的最大宽度,并为不使用最大字节的字符填充其余部分。
  • 我的字符集是 AL32UTF8。这显然是在 11.2.0.1.0 和 11.2.0.2.0 之间修复的错误。我会在 11.2.0.1.0 上进行测试,但在那个版本中我们没有任何数据库。
【解决方案3】:

正如您已经提到的,ORA-02293 错误的原因是因为它插入的是“N”(带有填充的空白)而不是“N”。所以你的约束被违反了。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-28
    • 1970-01-01
    • 2010-11-03
    • 2011-07-22
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多