2012-09-05 60 views
2

所以,我們只是發現,在我們的Oracle DBMS 254個表已一列名爲‘富’與錯誤的長度 - Number(10)代替Number(3)阿爾特多個表列長度​​

foo列是來自表中的PK的一部分。
這些表格還有其他帶有forigen鍵的表格。

我所做的是:

  1. 備份的表用一個臨時表。
  2. 禁用表格中的forigen鍵。
  3. 使用foo列禁用PK。
  4. 爲所有行確定foo列。
  5. 恢復上述所有

但是現在我們發現它不只是情侶對錶,但254表

有沒有簡單的方法,(或至少比這更容易)來改變列的長度?

P.S.我有DBA權限。

回答

3

有生成所需的腳本更簡單的方法,使用系統表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,這樣如果出現問題(例如沒有更多的表空間),則不會截斷未備份的內容。它可能幾乎值得逐步運行它,以便你知道一切都已正確完成。

我建議在不同的用戶上測試這個表格,以確保它滿足您的一切需求。

+0

謝謝!你能否給上面的_「看起來不看上去的」_代碼添加註釋。我不想運行我不完全理解的DDL查詢。 – gdoron

+0

我試圖讓它更容易理解......並添加了一些警告提示:) – Ben

+0

謝謝我非常感謝您的幫助和努力。最後一件事,'chr(10)'做了什麼?我從來沒有在查詢中看到過這樣的事情。 – gdoron

1

我們所做的是:

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

對於所有的表。

+0

沒有級聯刪除(例如)不需要的行爲?看起來相當危險(乍一看,但我可能會錯過一些東西)。 –

+0

@RaphaëlAlthaus。哦,是的,我必須禁用引用表中的所有外鍵。我會盡快將其添加到答案中。 {這是我錯過了什麼......;}} – gdoron

0

您的解決方案的工作原理很多,但意味着停工。

由於物理上NUMBER(3)酷似你可以添加CHECK約束和獲得無需停機同樣的邏輯限制更強的約束NUMBER(10)

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

這樣做的缺點是,所有插入和更新都必須更多地檢查該限制。 – Ben

+0

@ben:與大量工作(生成重做/撤銷,從緩存等獲取塊等,等等)相比,單一檢查約束產生了不可檢測的開銷量。 –

+0

它沒有幫助我,因爲我們讓它們成爲'NUMBER(3)'因爲我們開始使用'Entity Framework',它將一些映射到'short',一些映射到'int'。不管怎麼說,還是要謝謝你。 – gdoron