对于具有char语义的列,Oracle是否支持默认值?

时间:2012-03-26 16:12:10

标签: oracle oracle11g ddl

我试图将一个列添加到表中,但是使用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(1)),所以我不认为nls_length_semanatics的值是相关的。
  2. 将列添加为char(1个字符)后,新添加的“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。
  7. 感谢。

3 个答案:

答案 0 :(得分:5)

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

  • 首先,数据库字符集必须是可变宽度字符集(即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

答案 1 :(得分: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

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

希望有所帮助。

答案 2 :(得分:2)

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

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