目标:在存储过程中实现返回一个流水号的生产单号(并发情况下如何产生)
     规则:字母(1-5位)+日期(8位)+'-'+流水号(4位)
    例子:A20090501-0001
        A20090501-0002
        A20090502-0001
        A20090502-0002
        A20090502-0003
        A20090503-0001
        B20090503-0001    
        B20090503-0002 
   
基础设置表1
单据类别 字母  当前最大值  当前日期
销售单   A    1          20090503
采购单   B     2         20090503如何生成:销售单 销售单号请问各位高手,用存储过程如何实现呢.(提出有建设性的意见都有分) 
是否可以不用设置基础也可以实现的呢 (比较急,谢谢!~)

解决方案 »

  1.   


    最简单的,建一个序列好了,每次都去取序列号就可以了!    例子:A20090501-序列号-0001 
            A20090501-序列号-0002 
            A20090502-序列号-0001 
            A20090502-序列号-0002 
            A20090502-序列号-0003 
            A20090503-序列号-0001 
            B20090503-序列号-0001    
            B20090503-序列号-0002
      

  2.   

    如果不用序列号的话,那就必须建一张表来存储流水号,否则按照你的规则,很有可能一天之内有重复的数据,你的 A--->Z 日期 9999模式的数据量是有限制的,撑死了是5*9999条记录,如果你这一天的流水号>5*9999的话,你如何保证不重复呢?
      

  3.   

    支持楼上,像你这种情况,用SEQUENCE是最方便,简单,快捷的
      

  4.   


    一般的应用来说,锁是oracle自动控制的,你开发的时候,记得事务同步就可以了,别的不用考虑的。
      

  5.   

    如果每天极限是5*9999的话,说明并发的问题不会很严重,直接建一张编号表比较方便
    给你个例子
    CREATE TABLE TBL_MC_SEQ_CTL(
        SEQ_GEN_CD        CHAR(2)          NOT NULL,
        BUSS_ELE_DESC     VARCHAR2(50)     DEFAULT ' ' NOT NULL,
        GBL_SEQ_NO        NUMBER(15, 0)    DEFAULT 0 NOT NULL,
        SEQ_LENTH_QTY     NUMBER(2, 0)     DEFAULT 0 NOT NULL,
        MIN_SEQ_NO        NUMBER(15, 0)    DEFAULT 0 NOT NULL,
        MAX_SEQ_NO        NUMBER(15, 0)    DEFAULT 0 NOT NULL,
        CYC_FLG           CHAR(1)          NOT NULL,
        REG_USER_ID       CHAR(10)         DEFAULT ' ' NOT NULL,
        UPD_USER_ID       CHAR(10)         DEFAULT ' ' NOT NULL,
        REG_TMS           TIMESTAMP(6)     DEFAULT to_date('19000101000000','YYYYMMDDHH24MISS') NOT NULL,
        UPD_ONLINE_TMS    TIMESTAMP(6)     DEFAULT to_date('19000101000000','YYYYMMDDHH24MISS') NOT NULL,
        UPD_BATCH_TMS     TIMESTAMP(6)     DEFAULT to_date('19000101000000','YYYYMMDDHH24MISS') NOT NULL,
        DEL_TMS           TIMESTAMP(6)     DEFAULT to_date('19000101000000','YYYYMMDDHH24MISS') NOT NULL,
        DEL_FLG           CHAR(1)          NOT NULL
    )
    PCTFREE 5
    PCTUSED 80
    TABLESPACE MASTER_DATA
    STORAGE(INITIAL 64K
            NEXT 64K
            MINEXTENTS 1
            MAXEXTENTS UNLIMITED
            PCTINCREASE 0
            )
    ;CREATE UNIQUE INDEX PK_MC_SEQ_CTL ON TBL_MC_SEQ_CTL(SEQ_GEN_CD, BUSS_ELE_DESC)
    TABLESPACE MASTER_IDX
    STORAGE(INITIAL 64K
            NEXT 64K
            MINEXTENTS 1
            MAXEXTENTS UNLIMITED
            PCTINCREASE 0
            )
    ;
      

  6.   

    可以用before insert 触发器实现,在每条记录插入前,取出表中已有序列号,计算出新号,放到相应字段
      

  7.   

    如果不用基础设置表,那么 单子的字母从哪取呢?单子的时间也需要根据基础设置表来获得的吧(如果直接获取系统时间会不会有其他问题),所以我觉得还是用基础设置表比较好。用这个表就比较好办了,每次出入记录的时候,先更新基础设置表,并返回流水号就行了。
    update 基础设置表1 set 当前最大值 = 当前最大值 +1 where... return 当前最大值 into ..., 而且这样做你想要的并发就由oracle来控制了,不需要你考虑了。
      

  8.   

    oracle 会自动加锁,不用担心
    建个序列号表
    create table TBL_SEQ
    (
        seq_type varchar2(5) not null,
        current_date varchar2(8) not null,
        current_value number(4,0) not null
    );
    写个取序列号的函数
    create or replace function f_get_serial(pi_seq_type in varchar2)
     return varchar2 is
      Result varchar2(40);
    vc_nowdate varchar2(8);
    vc_seq_date varvhar2(8);
    vn_value;
    pragma autonomous_transaction;--自治事务,可以减少锁表的时间,提高并发能力
    begin
        select to_char(sysdate,'YYYYMMDD') into vc_nowdate from dual;--取当前日期
        update tbl_seq --序列号+1,同时锁住该条记录
           set current_value=current_value+1
         where seq_type=pi_seq_type
        returning current_value ,current_date into vn_value,vc_seq_date;
        ;
        if vc_seq_date<vc_nowdate then --如果序列号表中的日期比当前日期小,重置序列号,更新日期,这时候该条记录已经锁住,不用担心并发问题
            update tbl_seq
               set current_value=1,
                   current_date=vc_nowdate
             where seq_type=pi_seq_type
            returning current_value ,current_date into vn_value,vc_seq_date;
            ;
        end if;
        Result :=pi_seq_type||vc_seq_date||'-'||lpad(to_char(vn_value),4,'0');
        commit;--自治事务必须提交或回滚
        return Result ;
    end;
      

  9.   

    使用自治事务不好的地方就是如果调用它的那个过程回滚则会浪费序列号。如果严格要求不许跳号则去掉pragma autonomous_transaction;和commit;这样的话并发记录被锁住的时间比较长,并发性能较差。
    vn_value ;应该为 vn_value number;
      

  10.   

    谢谢yhuib 的参与与代码的解答!
    通过这几个对oracle写锁与读锁的初步了解.应用过程中确认不用去并发问题(一般的应用来说,锁是oracle自动控制的,你开发的时候,记得事务同步就可以了,别的不用考虑的。正如:oraclelogan 所说的)1.如果通过使用序列的话(任务中每天调度一次,重置为零),引申出一个问题就是如果过程的事务回滚后,就会浪费序列号,导致业务单据号产生断号(当然断号情况排除业务单号被删除的例外情况),但如何才能既能使用序列,又能避免断号情况.
    (因为我想用序列也许在并发情况下,效率也许会高过自身维护一个流水号值)2.设置基础表来维护流水号值,是否在并发情况下会慢些呢.不知有没有人对比测试过呢.3.从第一个问题产生另一个问题,如果每种单据都设置一个序列,那么又如何在一个存储过程中实现对多个序列进行重置呢.(这个疑问在网上找了,但没有收获,不知有没有人也有这种需求)
      

  11.   

    多谢楼上多位朋友回贴与交流!~~xmanliming 如何建立序列呢?跪求! 
     
    R:一个简单例子
    create sequence seq_1 increment by 1 start with 1 maxvalue 999999999; 
      

  12.   

    存储过程中重置序列号可以使用如下存储过程
    create or replace procedure P_TOOLS_RESET_SEQUENCES
    (
       pi_sequence_name   in varchar2
    )
    as
       vn_number       number;
       vr_sequence  user_sequences%rowtype;
    begin
    -- Modify the last number
      select * into vr_sequence from user_sequences t where t.sequence_name=pi_sequence_name;
      if vr_sequence.max_value-vr_sequence.last_number>0 then
           execute immediate 'alter sequence '||pi_sequence_name||' increment by '||(vr_sequence.max_value-vr_sequence.last_number)|| ' nocache';
           execute immediate 'select '||pi_sequence_name||'.nextval from dual' into vn_number;
           execute immediate 'alter sequence '||pi_sequence_name||' increment by 1 nocache';
           execute immediate 'select '||pi_sequence_name||'.nextval from dual' into vn_number;
      end if;
    exception
       when others then
          null;
    end P_TOOLS_RESET_SEQUENCES;
      

  13.   

    http://topic.csdn.net/u/20090525/22/f9a310d4-b5d3-4990-96c7-d5e3fe0fcbd6.html这个LZ可以看看的