【问题标题】:Alter multiple tables' columns length更改多个表的列长度
【发布时间】:2012-09-05 09:36:56
【问题描述】:

所以,我们刚刚发现 Oracle DBMS 中的 254 个表有一个名为 "Foo" 的列长度错误 - Number(10) 而不是 Number(3)

foo 列是表 PK 的一部分。
这些表还有其他带有外来键的表。

我所做的是:

  1. 使用临时表备份该表。
  2. 禁用了表的外来键。
  3. 使用 foo 列禁用 PK。
  4. 将所有行的 foo 列清空。
  5. 以上全部恢复

但现在我们发现这不仅仅是几张桌子,而是 254 张桌子

有没有一种简单的方法(或者至少比这更容易)来改变列的长度?

附:我有 DBA 权限。

【问题讨论】:

    标签: sql database oracle orm ddl


    【解决方案1】:

    有一种更简单的方法来生成您想要的脚本,使用系统表user_tablesuser_constraints 来动态生成 DDL。缺点是这需要停机时间。另请注意,我使用truncate 命令而不是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
    

    由于stage列的原因,这不会是逐表进行,而是逐阶段进行,这样所有的约束都会同时被禁用,这样可以避免出现问题。如果你害怕(我会害怕),那么从查询中删除_backup 表中的drop;这意味着无论出现什么问题,您都是安全的。

    如果您在 SQL*Plus 中运行它,您还希望包含 whenever sqlerror exit,这样如果出现问题,例如没有更多的表空间,您就不会截断尚未备份的内容。几乎值得分阶段运行它,以便您知道一切都已正确完成。

    我建议用几个表在不同的用户上测试它,以确保它可以满足您的所有需求。

    【讨论】:

    • 谢谢!您能否将 cmets 添加到上面的 “不可爱” 代码中。我不想运行我不完全理解的 DDL 查询。
    • 我试图让它更容易理解......并添加了一些警告说明:)
    • 非常感谢您的帮助和努力。最后一件事,chr(10) 在做什么?我从来没有在查询中看到过这样的事情。
    • 没问题我经常做类似的事情!它只是一个换行符(ASCII 10),chr() 对其进行编码。这意味着在打印结果时,连接在一起以避免大量联合的语句位于不同的行上。
    【解决方案2】:

    我们所做的是:

    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>;
    

    对于所有表格。

    【讨论】:

    • 没有级联删除(例如)不需要的行为?似乎很危险(乍一看,但我可能会漏掉一些东西)。
    • @RaphaëlAlthaus。哦,是的,我必须禁用引用表中的所有外键。我会尽快将其添加到答案中。 {是我错过了什么……;) }
    【解决方案3】:

    您的解决方案有效,但工作量很大,并且意味着停机。

    由于物理上 NUMBER(3) 与具有更强约束的 NUMBER(10) 完全相同,您可以添加 CHECK 约束并获得相同的逻辑限制而无需停机:

    LOOP
       ALTER TABLE <table_name> ADD CONSTRAINT <table_foo_chk> CHECK (foo < 1000);
    END LOOP;
    

    【讨论】:

    • 这样做的缺点是所有插入和更新都必须永远检查该约束。
    • @ben: 与大量工作(生成重做/撤消、从缓存中获取块等)相比,单一检查约束会产生无法检测的开销更新/插入。
    • 这对我没有帮助,因为我让它们成为NUMBER(3),因为我们开始使用Entity Framework,它映射一些到short,一些映射到int。无论如何,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-23
    • 2022-08-19
    • 2012-02-08
    • 1970-01-01
    • 2013-10-06
    相关资源
    最近更新 更多