2012-03-26 66 views
5

我试图添加一列到一个表,但得到了令人惊讶的效果与DEFAULT子句。在与现有行的表,我添加了一个新列,像这样:Oracle是否支持具有char语义的列的缺省值?

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个字符)后,新添加的“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。

谢谢。

+0

行如果只值是“N”或“Y” ,为什么不做(1字节)? – tbone 2012-03-26 16:25:04

+2

尝试重写你的检查约束为'CHECK(C IN(TO_NCHAR('N'),TO_NCHAR('Y')))'。我不确定在检查约束中是否允许调用函数,但它至少值得一试。 – 2012-03-26 16:51:54

回答

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 

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

希望有所帮助。

+0

有趣。我在Windows和Linux上都试过了,Oracle 11.2.0.1.0。所以也许这是一个小版本的bug。我会尝试更新。谢谢。 – 2012-03-26 17:26:37

+3

@Mark J. Bobak - 你的数据库字符集是什么?我的下注是,这只发生在具有可变宽度字符集的数据库中(即'AL32UTF8')。 – 2012-03-26 17:42:29

+0

@JustinCave FTW!我同意,我认为这是可变宽度字符集。 Oracle将不得不分配可变宽度字符集允许的最大宽度,并将其余字节填充为不使用最大字节的字符。 – 2012-03-26 17:44:16

2

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

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

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 
+3

另请参阅上面的示例和注释。这显然是11.2.0.1.0中的一个错误,并在11.2.0.2.0中修复。 – 2012-03-26 18:30:49