自动为Oracle中的所有表生成序列和触发器

时间:2017-02-28 09:58:57

标签: oracle triggers sequence dynamic-sql

在我的架构中,我将大约250个表从SQL Server迁移到Oracle。问题是,没有为任何这些表创建序列或触发器。

是否有一种简单的方法可以生成所有表序列和触发器,而不是手动为每个表执行此操作?

我需要的序列的一个例子是:

CREATE SEQUENCE "SYSTEM"."SEC_USERS_ID_SEQ"  
    MINVALUE 0 MAXVALUE 999999999999999999999999 
    INCREMENT BY 1 
    START WITH 23 
    CACHE 20 
    NOORDER NOCYCLE NOPARTITION;

触发器:

create or replace TRIGGER SEC_USERS_TRIG 
before INSERT 
ON "SYSTEM"."SEC_USERS" 
FOR EACH row 
BEGIN 
    IF inserting THEN 
       IF :NEW."ID" IS NULL THEN
          SELECT SEC_USERS_ID_SEQ.nextval INTO :NEW."ID" FROM dual;
       END IF;
    END IF;
END;

2 个答案:

答案 0 :(得分:2)

我们可以使用Oracle数据字典视图(相当于MSSQL INFORMATION_SCHEMA)生成脚本。 Find out more

此示例生成CREATE SEQUENCE语句。我已按照您的示例接受了默认值,但不需要对其进行编码。序列名称源自与列名连接的表名,后缀为" _SEQ"。注意Oracle's thirty character limit on object names

此循环动态查询表以获取主键列的当前最大值,该列用于派生STARTS WITH子句。

declare
    curr_mx number;
begin
    for lrec in ( select ucc.table_name
                         , ucc.column_name
                  from user_constraints uc
                       join user_cons_columns ucc
                           on ucc.table_name = uc.table_name
                          and ucc.constraint_name = uc.constraint_name
                       join user_tab_columns utc
                          on utc.table_name = ucc.table_name
                          and utc.column_name = ucc.column_name
                  where uc.constraint_type = 'P' -- primary key
                  and   utc.data_type = 'NUMBER' -- only numeric columns
                  )
    loop
        execute immediate 'select max ('|| lrec.column_name ||') from ' ||lrec.table_name 
            into curr_mx;
        if curr_mx is null then
            curr_mx := 0;
        end if;
        dbms_output.put_line('CREATE SEQUENCE "'|| user || '"."'
            || lrec.table_name ||'_'|| lrec.column_name || '_SEQ" '
            ||' START WITH ' || to_char( curr_mx + 1 )  ||';'
        );
    end loop;
end;
/

此代码使用DBMS_OUTPUT,因此您可以将其假脱机到文件中供以后使用。如果您使用的是像SQL Developer这样的IDE,则可能需要启用DBMS_OUTPUT。请遵循this StackOverflow answer中的指导。

如果您可以保证所有表都有一个主键,这是一个名为ID的数字列,那么您可以简化select语句。相反,如果你的一些主键是复合约束,你将需要处理它。

显然,我生成序列是因为它们更简单。编写更复杂的触发器实现是留给读者的练习:)

答案 1 :(得分:1)

感谢脚本。我稍微修改了它并执行了触发器。随意使用它。

declare
    curr_mx number;
    counter number;
    seq_name varchar2 (30);
    trigger_name varchar2 (30);
begin
    for lrec in ( select ucc.table_name
                         , ucc.column_name
                  from user_constraints uc
                       join user_cons_columns ucc
                           on ucc.table_name = uc.table_name
                          and ucc.constraint_name = uc.constraint_name
                       join user_tab_columns utc
                          on utc.table_name = ucc.table_name
                          and utc.column_name = ucc.column_name
                  where uc.constraint_type = 'P' -- primary key
                  and   utc.data_type = 'NUMBER' -- only numeric columns
                  )
    loop
        execute immediate 'select (max ('|| lrec.column_name ||')+1) from ' ||lrec.table_name 
            into curr_mx;

        IF curr_mx is null THEN
            curr_mx := 0;
        END IF;

        IF counter is null THEN
            counter := 0;
        END IF;    

        /* check length of sequence name, 30 is max */
        IF length(lrec.table_name ||'_'|| lrec.column_name || '_SEQ') > 30 THEN

            IF length(lrec.column_name || '_SEQ') > 30 THEN

                seq_name := counter || '_PKA_SEQ';

            ELSE

                seq_name := lrec.column_name || '_SEQ';

            END IF;  

        ELSE

            seq_name := lrec.table_name ||'_'|| lrec.column_name || '_SEQ';

        END IF;

        /* check length of trigger name, 30 is max */
        IF length(lrec.table_name || '_PKA_T') > 30 THEN

            trigger_name := counter || '_PKA_T'; 

        ELSE

            trigger_name := lrec.table_name || '_PKA_T';

        END IF;        

        counter := counter +1;

        dbms_output.put_line(
          'CREATE SEQUENCE "' || seq_name || '"'
          ||' START WITH ' || to_char( curr_mx + 1 )  ||';'
        );
        dbms_output.put_line('/');
        dbms_output.put_line(
          'CREATE OR REPLACE TRIGGER "' || trigger_name || '"' 
          || ' BEFORE INSERT ON "' || lrec.table_name || '"' 
          || ' FOR EACH ROW '
          || ' BEGIN '
          || ' :new."' || lrec.column_name || '" := "' || seq_name || '".nextval;'
          || ' END;'
        );
        dbms_output.put_line('/');

    end loop;
end;

我还检查了序列和触发器的名称是否超过30个字符,因为oracle不接受这些。

编辑: 不得不在每一行后加上'/',这样你就可以在一次运行中执行所有语句。