首页 文章

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

提问于
浏览
0

在我的架构中,我已将大约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 回答

  • 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

    谢谢你的剧本 . 我稍微修改了它并执行了触发器 . 随意使用它 .

    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不接受这些 .

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

相关问题