3

I've an Oracle database with two schemas. One is old and another is new. I would like to update the old schema with the new columns of the new schema.

I get the tables which have changes with the following query.

select distinct table_name
from
(
    select table_name,column_name
    from all_tab_cols
    where owner = 'SCHEMA_1'

    minus

    select table_name,column_name
    from all_tab_cols
    where owner = 'SCHEMA_2'
)

With this query I get the tables. How can I update the old schema tables with the new columns? I don't need the data, just the columns.

hakre
  • 193,403
  • 52
  • 435
  • 836
coder247
  • 2,913
  • 19
  • 50
  • 70
  • Be careful, your query will return columns that are new in `SCHEMA_1`, but you don't get columns that exist in `SCHEMA_2` but not in `SCHEMA_1`. And you don't find columns that have been altered. Good luck :) – Peter Lang Feb 25 '10 at 08:39
  • Thanks Peter Lang.. i know that i'm not an Oracle expert :) if anyone has some ideas how to do it. Please share it with me.... – coder247 Feb 25 '10 at 08:44
  • Does your new schema contain data? Is dropping and re-creating the tables an option? How many tables do you have? Did you only change columns, or are there also new tables? – Peter Lang Feb 25 '10 at 08:45
  • ya...both schema's contain data. Can we do it without dropping? There are around 80 tables in new schema.There are 3 new tables and many tables with new columns in new schema. – coder247 Feb 25 '10 at 08:53
  • There are quite a few solutions on the market (e.g. Toad, RedGate) that will do this for you? Are they an option or do you need a zero-cost solution? – Nick Pierpoint Feb 25 '10 at 09:18
  • hmmm...i think zero cost is better for my pocket. It's not heavy :) – coder247 Feb 25 '10 at 09:27
  • What about new tables in SCHEMA_1 that don't exist in SCHEMA_2? And new indexes? New constraints? Do you need a solution that handles all these changes? – Nick Pierpoint Feb 25 '10 at 09:39

3 Answers3

5

A schema comparison tool is a good idea. The database schema is far more complicated than most people give credit, and every difference between two database schemas has the potential to cause bugs.

If you're still keen to do it yourself, the best approach I've found is to extract the schema definitions to text, then run a text compare. As long as everything is sorted alphabetically, you can then use Compare Documents feature in Microsoft Word (or FC.EXE, DIFF or equivalent), to highlight the differences.

The following SQLPlus script outputs the schema definition alphabetically, to allow comparison. There are two sections. The first section lists each column, in the format:

table_name.column_name: data_type = data_default <nullable>

The second section lists indexes and constraints, as follows:

PK constraint_name on table_name (pk_column_list)
FK constraint_name on table_name (fk_column_list)
CHECK constraint_name on table_name (constraint_definition)

The script serves as a useful references for extracting some of the Oracle schema details. This can be good knowledge to have when you're out at client sites and you don't have your usual tools available, or when security policies prevent you from accessing a client site database directly from your own PC.

set serveroutput on;
set serveroutput on size 1000000;
declare
  rowcnt    pls_integer := 0;
  cursor c_column is
     select table_name, column_name, data_type, 
        data_precision, data_length, data_scale, 
        data_default, nullable,
        decode(data_scale, null, null, ',') scale_comma,
        decode(default_length, null, null, '= ') default_equals
      from all_tab_columns where owner = 'BCC'
      order by table_name, column_name;
  cursor c_constraint is
      select c.table_name, c.constraint_name,
         decode(c.constraint_type,
                'P','PK',
                'R','FK',
                'C','CHECK',
                 c.constraint_type) constraint_type,
         c.search_condition, 
         cc.column_1||cc.comma_2||cc.column_2||cc.comma_3||cc.column_3||cc.comma_4||cc.column_4||
         cc.comma_5||cc.column_5||cc.comma_6||cc.column_6||cc.comma_7||cc.column_7 r_columns   
       from all_constraints c,
          ( select owner, table_name, constraint_name, nvl(max(position),0) max_position,
             max( decode( position, 1, column_name, null ) ) column_1,
             max( decode( position, 2, decode(column_name, null, null, ',' ), null ) ) comma_2,
             max( decode( position, 2, column_name, null ) ) column_2,
             max( decode( position, 3, decode(column_name, null, null, ',' ), null ) ) comma_3,
             max( decode( position, 3, column_name, null ) ) column_3,
             max( decode( position, 4, decode(column_name, null, null, ',' ), null ) ) comma_4,
             max( decode( position, 4, column_name, null ) ) column_4,
             max( decode( position, 5, decode(column_name, null, null, ',' ), null ) ) comma_5,
             max( decode( position, 5, column_name, null ) ) column_5,
             max( decode( position, 6, decode(column_name, null, null, ',' ), null ) ) comma_6,
             max( decode( position, 6, column_name, null ) ) column_6,
             max( decode( position, 7, decode(column_name, null, null, ',' ), null ) ) comma_7,
             max( decode( position, 7, column_name, null ) ) column_7
           from all_cons_columns
           group by owner, table_name, constraint_name ) cc
       where c.owner = 'BCC'
       and c.generated != 'GENERATED NAME'
       and cc.owner = c.owner
       and cc.table_name = c.table_name
       and cc.constraint_name = c.constraint_name
       order by c.table_name, 
          decode(c.constraint_type,
                 'P','PK',
                 'R','FK',
                 'C','CHECK',
                 c.constraint_type) desc, 
          c.constraint_name;
begin
  for c_columnRow in c_column loop
    dbms_output.put_line(substr(c_columnRow.table_name||'.'||c_columnRow.column_name||': '||
                         c_columnRow.data_type||'('||
                         nvl(c_columnRow.data_precision, c_columnRow.data_length)||
                         c_columnRow.scale_comma||c_columnRow.data_scale||') '||
                         c_columnRow.default_equals||c_columnRow.data_default||
                         ' <'||c_columnRow.nullable||'>',1,255));
    rowcnt := rowcnt + 1;
  end loop;
  for c_constraintRow in c_constraint loop
    dbms_output.put_line(substr(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
                         c_constraintRow.table_name||' ('||
                         c_constraintRow.search_condition||
                         c_constraintRow.r_columns||') ',1,255));
    if length(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
                         c_constraintRow.table_name||' ('||
                         c_constraintRow.search_condition||
                         c_constraintRow.r_columns||') ') > 255 then
       dbms_output.put_line('... '||substr(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
                            c_constraintRow.table_name||' ('||
                            c_constraintRow.search_condition||
                            c_constraintRow.r_columns||') ',256,251));
    end if;
    rowcnt := rowcnt + 1;
  end loop;
end;
/

Unfortunately, there are a few limitations:

  1. Embedded carriage returns and whitespace in data_defaults, and check constraint definitions, may be highlighted as differences, even though they have zero effect on the schema.
  2. Does not include alternate keys, unique indexes or performance indexes. This would require a third SELECT statement in the script, referencing all_ind_columns and all_indexes catalog views.
  3. Does not include security details, synonyms, packages, triggers, etc. Packages and triggers would be best compared using an approach similar to the one you originally proposed. Other aspects of the schema definition could be added to the above script.
  4. The FK definitions above identify the referencing foreign key columns, but not the PK or the table being referenced. Just one more detail I never got around to doing.

Even if you don't use the script. There's a certain techie pleasure in playing with this stuff. ;-)

Matthew

  • Matthew, I am not an Oracle expert, so could you explain where is the output of your code ? I run it successfully but I don't know where is the result. – Helodia Feb 14 '17 at 09:49
3

I'm afraid I can't do more for you at the moment, but this should give you a basic idea.

It selects ADD and DROP column statements that you could execute after carefully reviewing them.

It does not handle

  • created/dropped tables
  • data type/precision changes of existing columns (ALTER TABLE MODIFY)
  • DEFAULT VALUES (so you can't apply it on a table with data when new column is NOT NULL)
  • Check constraints, Foreign Key constraints

I tried it with some basic data-types (NUMBER, VARCHAR2, DATE) and it worked. Good luck :)


SELECT    'ALTER TABLE ' || LOWER(table_name)
       || ' ADD ' || LOWER(column_name) || ' ' || data_type
       || CASE WHEN data_type NOT IN ('DATE') THEN '(' || data_length || ')' END
       || CASE WHEN nullable='Y' THEN ' NOT NULL' END
       || ';' cmd
  FROM all_tab_cols c2
 WHERE owner = 'SCHEMA_1'
   AND NOT EXISTS ( SELECT 1
                      FROM all_tab_cols c1
                     WHERE owner = 'SCHEMA_2'
                       AND c1.table_name = c2.table_name
                       AND c1.column_name = c2.column_name )
UNION ALL
SELECT    'ALTER TABLE ' || LOWER(table_name)
       || ' DROP COLUMN ' || LOWER(column_name) || ';'
  FROM all_tab_cols c2
 WHERE owner = 'SCHEMA_2'
   AND NOT EXISTS ( SELECT 1
                      FROM all_tab_cols c1
                     WHERE owner = 'SCHEMA_1'
                       AND c1.table_name = c2.table_name
                       AND c1.column_name = c2.column_name )
ORDER BY cmd;
Peter Lang
  • 54,264
  • 27
  • 148
  • 161
3

I started writing an answer for this but my list of caveats became longer than the answer so I decided to scrap it.

You should go for a schema comparison tool.

There are free versions available - take a look at this question on Server Fault:

https://serverfault.com/questions/26360/how-can-i-diff-two-oracle-10g-schemas

My suggestion would be to download Oracle's SQL Developer and use the built-in schema diff tool (although this requires that you have the Change Management Pack license).

Community
  • 1
  • 1
Nick Pierpoint
  • 17,641
  • 9
  • 46
  • 74
  • Yep :) My original answer was getting a little preachy. "You should have all schema changes in version controlled scripts and... blah blah". Backspace backspace. :) – Nick Pierpoint Feb 25 '10 at 09:57
  • Be careful with this suggestion: the SQL Developer schmea diff tool *is not free*. It requires a license for the Change Management Pack, which is a chargeable extra to the Enterprise Edition license. – APC Feb 25 '10 at 11:42
  • @Nick Pierpoint - Perhaps you should drag out the soapbox again, because all schema changes ought to be in source control. – APC Feb 25 '10 at 11:54
  • @APC Good point about the Change Management Pack license - I hadn't seen this before. Seems an odd limitation. I suppose the "diff" is using built-in packages in the Pack. So yes... out with the soap box. – Nick Pierpoint Feb 25 '10 at 12:33