修改(减少)列的长度时会发生什么?


10

可以说我有两列类型NUMBER(无精度和小数位数)和VARCHAR(300)。我看到这些列对于我的数据来说太大了,所以我想将它们修改为NUMBER(11)VARCHAR(10)。因此,如果我运行此SQL语句:

ALTER TABLE FOO
    MODIFY(BAR NUMBER(10));
  • 我可以在非空列上执行此操作吗?
  • 如果是这样,那么如果有一个大于的值NUMBER(10),oracle会告诉我吗?
  • 如果先前定义,列默认值是否会保持不变?
  • 列为空的选项会保持不变吗?
  • 该列上的主键,外键,唯一键是否保持不变?
  • 涉及该列的约束条件将保持不变吗?
  • 该列上的索引会保持不变吗?

是否有任何官方文件回答我的问题?

Answers:


12

Oracle管理员指南》说:

使用ALTER TABLE ... MODIFY语句修改现有的列定义。您可以修改列数据类型,默认值,列约束,列表达式(对于虚拟列)和列加密。

如果所有现有数据都满足新长度,则可以增加或减少现有列的长度。您可以将列从字节语义更改为CHAR语义,反之亦然。您必须设置初始化参数BLANK_TRIMMING = TRUE来减少非空CHAR列的长度。

如果要修改表以增加数据类型为CHAR的列的长度,请意识到这可能是一项耗时的操作,并且可能需要大量的额外存储,尤其是在表包含许多行的情况下。这是因为每行中的CHAR值都必须空白填充才能满足新的列长度。

Oracle SQL语言参考》提供了更多详细信息,包括以下内容:

如果列的所有行都包含空值,则可以更改任何列的数据类型。但是,如果更改实例化视图容器表中列的数据类型,则Oracle数据库会使相应的实例化视图无效。

无论所有行是否都包含空值,都可以始终增加字符或原始列的大小或数字列的精度。只要更改不需要修改数据,就可以减小列的数据类型的大小。如果存在的数据超过新的长度限制,则数据库将扫描现有数据并返回错误。

您可以将DATE列修改为TIMESTAMP或TIMESTAMP WITH LOCAL TIME ZONE。您可以将任何具有本地时区的TIMESTAMP修改为DATE列。

如果表为空,则可以增加或减少datetime或interval列的前导字段或小数秒值。如果表不为空,则只能增加日期时间或间隔列的前导字段或小数秒。

对于CHAR和VARCHAR2列,可以通过指定CHAR(指示最初以字节为单位指定的列的字符语义)或BYTE(指示最初以字符为单位指定的列的字节语义)来更改长度语义。要了解现有列的长度语义,请查询ALL_,USER_或DBA_TAB_COLUMNS数据字典视图的CHAR_USED列。

上述文档中还有其他信息和限制。这是尝试降低Number列的精度并减小Varchar2的长度的演示。您可以尝试其他更改,以便知道会发生什么。

--Setup.
DROP TABLE FOO;
CREATE TABLE FOO (BAR Number, BAR2 VARCHAR2(300));
INSERT INTO FOO (SELECT Level, RPAD(to_char(Level),10*Level,to_char(Level)) 
   FROM DUAL CONNECT BY Level <=20);
COMMIT;
SELECT * FROM FOO;

--Reduce Number to Number(10).
ALTER TABLE FOO MODIFY (BAR NUMBER (10));

--Reduce Varchar2(300) to Varchar2(100) (data would be truncated).
ALTER TABLE FOO MODIFY (BAR2 VARCHAR2(100));

--Reduce Varchar2(300) to Varchar2(200) (no data would be truncated).
ALTER TABLE FOO MODIFY (BAR2 VARCHAR2(200));

alter语句具有以下输出:

ALTER TABLE FOO MODIFY (BAR NUMBER (10))
Error report:
SQL Error: ORA-01440: column to be modified must be empty to decrease precision or scale
01440. 00000 -  "column to be modified must be empty to decrease precision or scale"

ALTER TABLE FOO MODIFY (BAR2 VARCHAR2(100))
Error report:
SQL Error: ORA-01441: cannot decrease column length because some value is too big
01441. 00000 -  "cannot decrease column length because some value is too big"

table FOO altered.

通过创建新列来降低精度。

ALTER TABLE FOO ADD (BAR3 NUMBER(10));
UPDATE FOO SET Bar3 = Bar;
ALTER TABLE FOO DROP COLUMN BAR;
ALTER TABLE FOO RENAME COLUMN BAR3 TO BAR;

因此,结论是-如果您想降低列的精度或规模并保留索引,键等内容,那么唯一的方法是复制表,截断表,更改类型,将数据复制回表并删除临时表。有没有更快,更优雅的方式?
mnowotka

1
好了,您可以创建一个新列,复制数据,重新创建索引,删除旧列并重命名新列。您也可以使用DBMS_REDEFINITION,也可以创建一个新表,复制数据,删除旧表并重命名新表。或者,您可以导出表,删除表,使用新定义重新创建表并导入数据。有很多方法可以做到这一点,但是您必须决定更快/更优雅。
Leigh Riffel

可能还可以创建一个新列,将数据复制到该列,将旧列设置为null,修改其长度,将数据从新列复制回更改后的旧列,然后删除新列。所有这些都是因为oracle不允许减少数字列,即使数据适合也是如此。8- {
汉斯·彼得·斯托尔2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.