Изменить длину столбцов нескольких таблиц

Итак, мы только что обнаружили, что 254 таблицы в нашей Oracle СУБД имеют один столбец с именем "Foo" с неправильной длиной — Number(10) вместо Number(3).

Этот столбец foo является частью PK таблиц.
В этих таблицах есть другие таблицы с чужими ключами.

Что я сделал:

  1. сделал резервную копию таблицы с помощью временной таблицы.
  2. Отключены внешние ключи к таблице.
  3. Отключил ПК со столбцом foo.
  4. Обнулен столбец foo для всех строк.
  5. Восстановил все вышеперечисленное

Но теперь мы выяснили, что это не просто пара таблиц, а 254 таблицы.

Есть ли простой способ (или, по крайней мере, проще этого) изменить длину столбцов?

P.S. У меня есть права DBA.


person gdoron is supporting Monica    schedule 05.09.2012    source источник


Ответы (3)


Есть более простой способ создать нужные сценарии, используя системные таблицы user_tables и user_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 это будет выполняться не таблица за таблицей, а поэтапно, так что все ограничения будут отключены одновременно, что позволит избежать проблем. Если вы боитесь (я бы испугался), удалите drop из _backup таблиц из запроса; это означает, что что бы ни пошло не так, вы в безопасности.

Если вы запускаете это в SQL*Plus, вы также хотите включить whenever sqlerror exit, чтобы в случае возникновения проблемы, например, отсутствия больше табличного пространства, вы не усекали то, для чего у вас нет резервной копии. Возможно, стоит запускать его поэтапно, чтобы вы знали, что все выполнено правильно.

Я бы предложил протестировать это на другом пользователе с несколькими таблицами, чтобы убедиться, что он делает все, что вам нужно.

person Ben    schedule 05.09.2012
comment
Спасибо! Не могли бы вы добавить комментарии к непривлекательно выглядящему коду выше. Я не хочу запускать DDL-запросы, которые не совсем понимаю. - person gdoron is supporting Monica; 05.09.2012
comment
Я постарался сделать его более понятным... И добавил несколько предостережений :) - person Ben; 05.09.2012
comment
Спасибо, я очень ценю вашу помощь и усилия. И последнее, что делает chr(10)? Я никогда не видел такого в запросе. - person gdoron is supporting Monica; 06.09.2012
comment
Нет проблем, я делаю подобные вещи регулярно! Это просто перевод строки (ASCII 10), chr() кодирует его. Это означает, что операторы, которые объединены вместе, чтобы избежать большого количества объединений, находятся в разных строках при выводе результатов. - person Ben; 06.09.2012

Что мы сделали:

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

Для всех столов.

person gdoron is supporting Monica    schedule 05.09.2012
comment
Нет каскадного удаления (например) нежелательного поведения? Кажется довольно опасным (на первый взгляд, но я могу что-то упустить). - person Raphaël Althaus; 05.09.2012
comment
@RaphaëlAlthaus. О, да, мне пришлось отключить все внешние ключи в таблицах, на которые ссылаются. Я добавлю это к ответу в ближайшее время. {И это я что-то пропустил... ;) } - person gdoron is supporting Monica; 05.09.2012

Ваше решение работает, но требует много работы и подразумевает время простоя.

Поскольку физически NUMBER(3) точно такой же, как NUMBER(10) с более сильным ограничением, вы можете добавить ограничения CHECK и получить такое же логическое ограничение без простоев:

LOOP
   ALTER TABLE <table_name> ADD CONSTRAINT <table_foo_chk> CHECK (foo < 1000);
END LOOP;
person Vincent Malgrat    schedule 05.09.2012
comment
Недостатком этого является то, что все вставки и обновления должны будут постоянно проверяться на соответствие этому ограничению. - person Ben; 05.09.2012
comment
@ben: ограничение одиночной проверки приводит к необнаружимому количеству накладных расходов по сравнению с огромным объемом работы (генерация повторов/отмен, получение блоков из кеша и т. д., т. д. и т. д.), подразумеваемой обновлением/вставкой. - person Vincent Malgrat; 05.09.2012
comment
Мне это не помогает, потому что они должны быть NUMBER(3), потому что мы начали использовать Entity Framework, и некоторые из них сопоставляются с short, а некоторые с int. Спасибо, в любом случае. - person gdoron is supporting Monica; 05.09.2012