zoukankan      html  css  js  c++  java
  • 【Oracle】仿Oracle Sequence的自定义年份Sequence(适合任何数据库)

    Oracle的Sequence很好用,自增,不用维护是它最大的特点,但碰到以下情况就比较麻烦了。

    系统开发时常被要求业务流水号的形式为年份+流水号,如20100001,每年的流水号都必须重1开始。

    上面的需求如通过Sequence实现方法有多种:

    方法1:只使用一个Sequence,程序判断年份,需要在年后第一天使用前重置Sequence到最小值。

    优点:开发方便,不需修改程序。

    缺点:需要人工干预,且要有很强的重置Sequence意识,不能直观反映出上一年Sequence的使用情况。

    方法2:使用多个Sequence,每年使用一个Sequence,程序根据年份使用不同的Sequence,或程序只适用一个Sequence名称,通过手工干预更改当前使用Sequence名称。

    优点:比方法1相对灵活,可不需修改程序,可反映历年Sequence使用情况。

    缺点:同样需要人工干预,且也需要在年后第一天使用前更改程序或更改当前使用Sequence名称。

    综上所见,Sequence在这里使用不太正确,鉴于这种情况,我想出了使用表和函数模拟出Sequence的方法。

    (以下方法同样可以在其他数据库上根据实际情况修改语法实现,这里以Oracle为例)

    首先表记录的是Sequence的使用情况,参考Oracle的Sequence内容,设计如下:

    YEAR:年份

    MINVALUE:最小值

    MAXVALUE:最大值

    NEXTNUMBER:当前值

    INCREASEBY:增量

    CYCLE:是否循环

    create table IDSEQUENCE
    (
      YEAR       NUMBER not null,
      MINVALUE   NUMBER not null,
      MAXVALUE   NUMBER not null,
      NEXTNUMBER NUMBER not null,
      INCREASEBY NUMBER not null,
      CYCLE      NUMBER not null
    )
    

    实现类似NextVal的函数:

    create or replace function IDNextval return number is
      PRAGMA AUTONOMOUS_TRANSACTION; --Oracle函数中需要添加此参数才能在函数中执行SQL
      Result Number;
      lYear number;
      cursor idsequence_cursor(nYear in varchar2) is select * from idsequence where year=nYear;
      refidsequence idsequence_cursor%rowtype;
    begin
      lYear := to_char(sysdate, 'yyyy');
      Result := -1;
      
      open idsequence_cursor(lYear);
      fetch idsequence_cursor into refidsequence;
      if not idsequence_cursor%notfound then
        Result := refidsequence.nextnumber;
        
        if refidsequence.nextnumber >= refidsequence.minvalue then --当前值大于等于最小值
          if refidsequence.nextnumber >= refidsequence.maxvalue then --当前值大于等于最大值
            if refidsequence.cycle = 1 then --循环则当前值等于最小值
              update idsequence set nextnumber=minvalue where year=lYear;
            else --不循环则当前值为最小值-1,即无效值
              update idsequence set nextnumber=minvalue-1 where year=lYear;
            end if;
          else --当前值大于等于最小值小于最大值
            if refidsequence.nextnumber+refidsequence.increaseby > refidsequence.maxvalue then --当前值加增量大于最大值
              update idsequence set nextnumber=minvalue where year=lYear;
            else  
              update idsequence set nextnumber=nextnumber+reffeesdocidsequence.increaseby where year=lYear;
            end if;
          end if;  
          commit;
        end if;
      end if;
      close idsequence_cursor;
      
      return(Result);
    end IDNextval;
    
    

     实现类似CurrVal的函数:

    create or replace function IDCurrval return number is
      Result Number;
      lYear number;
      lCount number;
    begin
      Result := -1;
      lYear := to_char(sysdate, 'yyyy');
      
      select count(1) into lCount from idsequence where year=lYear;
      if lCount > 0 then
        select nextnumber into Result from idsequence where year=lYear;
      end if;
    
      return(Result);
    end IDCurrval;
    

    使用的时候,用select idnextval from dual获取下一值,用select idcurrval from dual获取当前值。

    对于文章开头需求的应用,只需要在开发前把年份记录一次性添加够就行了,在年份切换时函数自动会使用相应年份的记录。

    上面函数有一点需要注意,当当前值大于最大值,且循环开关没设置(CYCLE)时,当前值置为最小值-1,即无效值。

    同样,当年份记录没有初始化时,获取到的当前值是-1。

  • 相关阅读:
    活动安排问题
    CodeForces
    HDU
    HDU
    HihoCoder
    二分签到题--二分入门
    并查集,最小生成树
    spark和 mapreduce的比较
    SparkSQL--数据源Parquet的加载和保存
    SparkSQL -DataFrame与RDD的互转
  • 原文地址:https://www.cnblogs.com/litou/p/2227860.html
Copyright © 2011-2022 走看看