Oracle中非主键列的自动增量

时间:2016-07-28 09:14:07

标签: sql oracle plsql oracle11g

我希望插入特定列的记录,只要根据以下条件将新行插入表中,该列就会递增1 对于当前年份,第一行的值为:1 对于当前年份,列值应增加1 意味着当前年度的第一条记录 1,匹配年份的下一个可用数字

Year  Value
2016    1
2016    2
2016    3
2017    1
2017    2
...

我的方法有点像这样:

INSERT INTO ABC(ANALYSIS_YEAR,ANALYSIS_NUMBER)
values (EXTRACT(YEAR FROM sysdate),
        case when ANALYSIS_YEAR=EXTRACT(YEAR FROM sysdate) then AutoIcreamt with starting value 1 else 1;
    )

2 个答案:

答案 0 :(得分:2)

任何查看当前表值的解决方案都不适用于真实的'具有多个用户,多个会话和并行事务的环境。

我认为您需要将这两项要求分开:

  1. 能够根据创建时间对记录进行排序
  2. 能够在一年内报告记录排序。
  3. 第一个是使用序列处理的,因为这些是为此设计的并且处理并发(多个用户,多个事务......)。

    第二个是报告要求,并且根据性能要求有许多选项。

    首先创建一个序列:

    create sequence seq_analysis_id start with 1 increment by 1 nocache nocycle;
    

    不要让我们创建一个基表和触发器来处理自动增量:

    create table analysis_data (
        analysis_id integer not null,
        analysis_date date not null
        );
    
    alter table analysis_data add constraint pk_analysis_data primary key (analysis_id);
    
    create or replace trigger trg_analysis_data
    before insert on analysis_data
    for each row
    begin
        :new.analysis_id := seq_analysis_id.nextval();
    end trg_analysis_data;
    /
    
    insert into analysis_data (analysis_date) values (to_date('2015-12-28', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2015-12-29', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2015-12-30', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2015-12-31', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2016-01-01', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2016-01-02', 'YYYY-MM-DD'));
    
    insert into analysis_data (analysis_date) values (to_date('2016-01-03', 'YYYY-MM-DD'));
    
    commit;
    
    select * from analysis_data;
    
    ANALYSIS_ID    ANALYSIS_DATE
        1           28/12/2015
        2           29/12/2015
        3           30/12/2015
        4           31/12/2015
        5           01/01/2016
        6           02/01/2016
        7           03/01/2016
    

    好的 - 所以一切正常,但不能满足你的要求:)

    这是第二部分 - 报告要求:

    第一个选项是获取动态需要的数字:

    select
        analysis_id,
        analysis_date,
        extract(year from analysis_date) analysis_year,
        row_number()
            over (partition by trunc(analysis_date, 'YYYY')
            order by analysis_date, analysis_id) analysis_number
    from
        analysis_data;
    

    使用分析函数(在这种情况下为row_number)是处理此类事情的好方法。

    ANALYSIS_ID ANALYSIS_DATE   ANALYSIS_YEAR    ANALYSIS_NUMBER
        1         28/12/2015      2015              1
        2         29/12/2015      2015              2
        3         30/12/2015      2015              3
        4         31/12/2015      2015              4
        5         01/01/2016      2016              1
        6         02/01/2016      2016              2
        7         03/01/2016      2016              3
    

    我已在analysis_date, analysis_id函数中row_number订购了。这可能不是必要的,但如果您必须处理analysis_date的更新(在这种情况下,序列本身不再适用于年度排序),则可能需要这样做。

    通过将其包装在视图中,您可以使报告更直接:

    create or replace view analysis_data_v as
    select
        analysis_id,
        analysis_date,
        extract(year from analysis_date) analysis_year,
        row_number()
            over (partition by trunc(analysis_date, 'YYYY')
            order by analysis_date, analysis_id) analysis_number
    from
        analysis_data;
    

    这可能就是您所需要的,但如果您拥有大型数据集,则可能需要预先计算其中一些值。您有11g的虚拟列,但这些不适用于分析功能。我在这里的选择是使用物化视图 - 处理物化视图刷新的方法很多,最简单的方法是:

    create materialized view analysis_data_mv 
        build immediate
        refresh complete on demand
    as 
        select
            analysis_id,
            analysis_date,
            analysis_year,
            analysis_number
        from
            analysis_data_v;
    
    select * from analysis_data_mv order by analysis_year, analysis_number;
    
    ANALYSIS_ID ANALYSIS_DATE   ANALYSIS_YEAR    ANALYSIS_NUMBER
        1         28/12/2015      2015              1
        2         29/12/2015      2015              2
        3         30/12/2015      2015              3
        4         31/12/2015      2015              4
        5         01/01/2016      2016              1
        6         02/01/2016      2016              2
        7         03/01/2016      2016              3
    

    在这种情况下,将手动刷新实体化视图:

    exec dbms_mview.refresh('analysis_data_mv');
    

    希望这有帮助。

答案 1 :(得分:0)

你可能需要一个看起来像这样的插入触发器(我猜你不能改变Year的值,在这种情况下它会变得更复杂)。 喜欢@ ibre5041这不适用于多用户环境,因为如果在同一年同时执行一些交易,你可能会有重复:

CREATE OR REPLACE TRIGGER trg_bi_table1
before insert
ON table_1
Begin
  select nvl(max(value),0)+1 as value
    into :new.value
    from table_1
   where Year = :new.Year;
end;
/

在多用户环境中,您应该使用select for update