因此,我们刚刚发现我们的OracleDBMS 中的 254 个表有一个名为“Foo”的列,其长度错误,Number(10)而不是Number(3).
该foo列是表 PK 的一部分。
这些表还有其他带有外来键的表。
我所做的是:
- 用临时表备份表。
- 禁用表的外来键。
- 禁用了与
foo列的PK。 - 为所有行清空
foo列。 - 恢复了以上所有
但现在我们发现这不仅仅是几张桌子,而是254 张桌子。
有没有一种简单的方法(或者至少比这更容易)来改变列的长度?
PS我有DBA权限。
有一种更简单的方法可以生成所需的脚本、使用系统表user_tables并user_constraints动态生成 DDL。缺点是这需要停机时间。另请注意,我使用truncatecommand 而不是delete,这应该更快。
假设一个简单的表看起来像:
create table a (
foo number(10)
, bar number(10)
, constraint pk_a primary key (foo)
, constraint fk_a foreign key ( bar ) references a(foo )
);
这个看起来不好看的查询
select cmd
from (
select table_name
, 1 as stage -- Just used to order by at the end.
, 'create table ' || table_name || '_backup as select * from '
|| table_name || ';' || chr(10) as cmd
-- chr(10) is LF
from user_tab_columns -- View of all columns
where column_name = 'FOO'
and data_precision = 10 -- Length of the number
union all
select table_name
, 3 as stage
, 'truncate table ' || table_name || ';' || chr(10) -- Remove all data
|| 'alter table ' || table_name
|| ' modify ( foo number(3));' || chr(10)
|| 'insert into ' || table_name || ' select * from '
|| table_name || '_backup;' || chr(10)
|| 'drop table ' || table_name || '_backup;' as cmd
from user_tab_columns
where column_name = 'FOO'
and data_precision = 10
union all
select ut.table_name
, 2 as stage
-- Disable the constraint
, 'alter table ' || uc.table_name || ' disable constraint '
|| uc.constraint_name || ';' || chr(10) as cmd
from user_constraints uc -- All named constraints
join user_tab_columns ut
on uc.table_name = ut.table_name
where ut.column_name = 'FOO'
and ut.data_precision = 10
and constraint_type = 'R' -- Foreign Key constraints (see link)
union all
select ut.table_name
, 4 as stage
, 'alter table ' || uc.table_name || ' enable constraint '
|| uc.constraint_name || ';' || chr(10) as cmd
from user_constraints uc
join user_tab_columns ut
on uc.table_name = ut.table_name
where ut.column_name = 'FOO'
and ut.data_precision = 10
and constraint_type = 'R'
)
order by stage
将产生以下内容:
create table A_backup as select * from A; -- Create your backup
alter table A disable constraint FK_A; -- Disable FKs
truncate table A; -- Remove all data in the table
alter table A modify ( foo number(3)); -- Reduce the size of the column
insert into A select * from A_backup; -- Replace all the data
drop table A_backup; -- Drop the backup
alter table A enable constraint FK_A; -- Re-enable FKs
由于 column stage,这不会逐表而是逐阶段完成,以便同时禁用所有约束,这将避免问题。如果您害怕(我会害怕),请从查询中删除drop表_backup;这意味着无论出现什么问题,您都是安全的。
如果您在 SQL*Plus 中运行它,您还希望将其包括在内whenever sqlerror exit,这样如果出现问题,例如没有更多的表空间,您就不会截断尚未备份的内容。几乎值得分阶段运行它,以便您知道一切都已正确完成。
我建议用几个表对不同的用户进行测试,以确保它可以满足您的所有需求。
我们所做的是:
CREATE TABLE <table_name_backup> as SELECT * <table_name>;
DELETE <table_name>;
ALTER TABLE <table_name> MODIFY (Foo NUMBER(3));
INSERT INTO <table_name> SELECT * FROM <table_name_backup>;
DROP <table_name_backup>;
对于所有的表。
您的解决方案有效,但工作量很大,并且意味着停机。
由于物理NUMBER(3)上 a 与具有更强约束的 a 完全相同NUMBER(10),您可以添加CHECK约束并获得相同的逻辑限制而无需停机:
LOOP
ALTER TABLE <table_name> ADD CONSTRAINT <table_foo_chk> CHECK (foo < 1000);
END LOOP;