悟心

成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。 上人生的旅途罢。前途很远,也很暗。然而不要怕。不怕的人的面前才有路。

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  93 随笔 :: 1 文章 :: 103 评论 :: 0 Trackbacks
项目中需要使用到按规则生成序列号,开始的解决办法是根据条件取得最大ID号进行分解+1再合并的解决方案,后来发现如果两个客户同时操作的时候出现问题:添加的时候ID已经存在,原因是:A用户在访问取得第MAX(N)记录deID,但是还没有进行增加操作,这时,B用户也取得第MAX(N)记录deID,这是A进行添加操作同时,B也同时进行添加操作,那么B用户就会保存失败,我们开始的时候采用的解决方案是:(枷锁)在程序的方法上枷锁,采用单线程操作,实施结果:失败,客户等待时间长,后来我们采用绑定数据库的方案:编写存储过程,虽然绑定不怎么方便,但是执行效率要快很多。由于时间问题,没有做序列自动创建功能,欢迎各位大虾进行指点和评论,我们最后的方案如下:
在oracle里面编写了2个基本做基本处理的自定义函数:
create or replace function fun_config_glide_num(p_class  varchar2,
                                                p_column 
varchar2)
/**
  * 流水帐号配置_oracle
  * author:ai bo 2010.02.23
  *  p_table varchar2 表名
  *  p_website varchar2 自定义开头字符
  
*/
 
return varchar2 as
  v_curId 
integer;
  
--  v_seq       varchar2(50);
  -- v_seq_value number;
  v_sql     varchar2(200);
  v_ret     
number(3);
  v_num     
varchar2(20);
  p_table   
varchar2(30);
  p_website 
varchar2(20);
  p_length  
varchar2(3);
  t_class   
varchar2(20);
  t_column  
varchar2(20);
begin
  t_class  :
= p_class;
  t_column :
= p_column;
  v_sql    :
= 'SELECT lszh_tname,lszh_mark,lszh_num FROM lszh_table WHERE lszh_cname = ' ||
              chr(
39|| t_class || chr(39|| '
 and lszh_column=
' || chr(39|| t_column || chr(39|| '';
  v_curId  :
= DBMS_SQL.OPEN_CURSOR; --为处理打开光标
  DBMS_SQL.PARSE(v_curId, v_Sql, DBMS_SQL.native); --分析语句
  DBMS_SQL.DEFINE_COLUMN(v_curId, 1, p_table, 128); --定义动态游标所能得到的对应值
  DBMS_SQL.DEFINE_COLUMN(v_curId, 2, p_website, 128);
  DBMS_SQL.DEFINE_COLUMN(v_curId, 
3, p_length, 128);
  v_ret :
= DBMS_SQL.EXECUTE(v_curId); --执行语句

  loop
    
if DBMS_SQL.FETCH_ROWS(v_curId) = 0 then
      
exit;
    
end if;
    DBMS_SQL.COLUMN_VALUE(v_curId, 
1, p_table); --将所取得的游标数据赋值到相应的变量
    --DBMS_OUTPUT.PUT_LINE(p_table);
    DBMS_SQL.COLUMN_VALUE(v_curId, 2, p_website);
    
--DBMS_OUTPUT.PUT_LINE(p_website);
    DBMS_SQL.COLUMN_VALUE(v_curId, 3, p_length);
    
--DBMS_OUTPUT.PUT_LINE(p_length);
  end loop;
  DBMS_SQL.CLOSE_CURSOR(v_curid); 
--关闭一个动态游标
  v_num := fun_get_glide_num(trim(p_table), trim(p_website), trim(p_length));
  
return v_num;
end;


create or replace function fun_get_glide_num(p_table   varchar2,p_website varchar2,p_length number)
/**
* 流水帐号创建oracle版本
* author : ai bo 2010.02.23
*  p_table varchar2 表名
*  p_website varchar2 自定义开头字符
*/
return varchar2 as
   v_seq       
varchar2(50);
   v_seq_value 
number;
    v_sql       
varchar2(200);
   v_num       
varchar2(20);
begin
    v_seq :
= 'seq_' || p_table;
    v_sql :
= 'select ' || v_seq || '.nextval from dual';
    dbms_output.put_line(v_sql);
   
execute immediate v_sql
       
into v_seq_value;
   
select lpad(v_seq_value, p_length, '0'into v_num from dual;
   v_num :
= p_website || v_num;
    
return v_num;
end;

下面为调用实例:
select fun_config_glide_num('TabMachineType','FAC_CODE'as CN FROM dual;

下面是表流水帐号表结构

-- Create table
create table LSZH_TABLE
(
  LSZH_CNAME    
CHAR(20not null,
  LSZH_TNAME    
CHAR(20),
  LSZH_COLUMN   
CHAR(20not null,
  LSZH_MARK     
CHAR(20),
  LSZH_DATETYPE 
CHAR(10),
  LSZH_NUM      
CHAR(3default 4
)

-- Add comments to the columns 
comment on column LSZH_TABLE.LSZH_CNAME
  
is '流水帐号类名';
comment 
on column LSZH_TABLE.LSZH_TNAME
  
is '流水帐对应的数据库表名';
comment 
on column LSZH_TABLE.LSZH_COLUMN
  
is '流水帐号类别标识列';
comment 
on column LSZH_TABLE.LSZH_MARK
  
is '流水帐号类别标识对应的字符';
comment 
on column LSZH_TABLE.LSZH_DATETYPE
  
is '时间作为流水号的排列次序';
comment 
on column LSZH_TABLE.LSZH_NUM
  
is '流水码长度';
-- Create/Recreate primary, unique and foreign key constraints 
alter table LSZH_TABLE
  
add constraint PK_LSZH_TABLE primary key (LSZH_COLUMN, LSZH_CNAME)

参考资料:
DBMS_SQL系统包提供了很多函数及过程,现在简要阐述其中使用频率较高的几种:   
    
  function   open_cursor:打开一个动态游标,并返回一个整型;   
    
  procedure   close_cursor(c   in   out   integer);关闭一个动态游标,参数为open_cursor所打开的游标;   
    
  procedure   parse(c   in   integer,   statement   in   varchar2,   language_flag   in   integer):对动态游标所提供的sql语句进行解析,参数C表示游标,statement为sql语句,language-flag为解析sql语句所用oracle版本,一般有V6,V7跟native(在不明白所连database版本时,使用native);   
    
  procedure   define_column(c   in   integer,   position   in   integer,   column   any   datatype,   [column_size   in   integer]):定义动态游标所能得到的对应值,其中c为动态游标,positon为对应动态sql中的位置(从1开始),column为该值所对应的变量,可以为任何类型,column_size只有在column为定义长度的类型中使用如VARCHAR2,CHAR等(该过程有很多种情况,此处只对一般使用到的类型进行表述);   
    
  function   execute(c   in   integer):执行游标,并返回处理一个整型,代表处理结果(对insert,delete,update才有意义,而对select语句而言可以忽略);   
    
  function   fetch_rows(c   in   integer):对游标进行循环取数据,并返回一个整数,为0时表示已经取到游标末端;   
    
  procedure   column_value(c   in   integer,   position   in   integer,   value):将所取得的游标数据赋值到相应的变量,c为游标,position为位置,value则为对应的变量;   
    
  procedure   bind_variable(c   in   integer,   name   in   varchar2,   value):定义动态sql语句(DML)中所对应字段的值,c为游标,name为字段名称,value为字段的值;   
    
  以上是在程序中经常使用到的几个函数及过程,其他函数及过程请参照oracle所提供定义语句dbmssql.sql   
    
    
  对于一般的select操作,如果使用动态的sql语句则需要进行以下几个步骤:   
  open   cursor--->parse--->define   column--->excute--->fetch   rows--->close   cursor;   
  而对于dml操作(insert,update)则需要进行以下几个步骤:   
  open   cursor--->parse--->bind   variable--->execute--->close   cursor;   
  对于delete操作只需要进行以下几个步骤:   
  open   cursor--->parse--->execute--->close   cursor;   
序列sequence:
http://www.examda.com/Oracle/
关于Oracle的序列(Sequence)使用序列是一数据库对象,利用它可生成唯一的整数。一般使用序列自动地生成主键值。对我们程序员来讲,精力时间有限,我们只学最有用的知识。大家请看:
  
1) 建立序列命令
  
CREATE SEQUENCE [user.]sequence_name
  
[increment by n]
  
[start with n]
  
[maxvalue n | nomaxvalue]
  
[minvalue n | nominvalue];
  
[NOCYCLE]  --
  INCREMENT BY: 指定序列号之间的间隔,该值可为正的或负的整数,但不可为0.序列为升序。忽略该子句时,缺省值为1.
  START 
WITH:指定生成的第一个序列号。在升序时,序列可从比最小值大的值开始,缺省值为序列的最小值。对于降序,序列可由比最大值小的值开始,缺省值为序列的最大值。
  MAXVALUE:指定序列可生成的最大值。
  NOMAXVALUE:为升序指定最大值为1027,为降序指定最大值为
-1.
  MINVALUE:指定序列的最小值。
  NOMINVALUE:为升序指定最小值为1.为降序指定最小值为
-1026.
  NOCYCLE:一直累加,不循环
  
2) 更改序列命令
  ALTERSEQUENCE 
[user.]sequence_name
  
[INCREMENT BY n]
  
[MAXVALUE n| NOMAXVALUE ]
  
[MINVALUE n | NOMINVALUE]
  修改序列可以:修改未来序列值的增量。
  设置或撤消最小值或最大值。
  改变缓冲序列的数目。
  指定序列号是否是有序。
  注意:
  
1,第一次NEXTVAL返回的是初始值
  
2,可以alter除start至以外的所有sequence参数。如果想要改变start值,必须 drop sequence 再 re-create .
  
3) 删除序列命令
  
DROP SEQUENCE [user.]sequence_name;
  用于从数据库中删除一序列。
  
4)牛刀小试
  
4.1)创建一个序列号的语句:
  
-- Create sequence
  create sequence NCME_QUESTION_SEQ
  minvalue 
1
  maxvalue 
999999999999
  start 
with 1
  increment 
by 1
  nocache;
  
//////////////////////////////
  
4.2)SQL中取序列号的用法:
  
SELECT NCME_QUESTION_SEQ.nextval FROM dual
  
SELECT NCME_QUESTION_SEQ.CURRVAL FROM dual
  
SELECT NCME_QUESTION_SEQ.nextval FROM dual SELECT NCME_QUESTION_SEQ.CURRVAL FROM dual
  注意:在使用序列的时候,有时需要有用户名,就像这样:
  
insert into system.CONSERVATOR(CONSERVATORNAME,CONPASS,CONTRUENAME,CONSEX,CONID)values('JG','123456','000',0, system.CONID.nextval);
参考资料链接:
关于DBMS_SQL包,动态SQL语句的问题
oracle 隐式游标,显示游标,游标循环,动态SELECT语句和动态游标,异常处理,自定义异常
oracle 创建,删除存储过程,参数传递,创建,删除存储函数,存储过程和函数的查看,包,系统包
几种使用动态SQL做游标的总结
关于动态SQL的使用
oracle创建存储过程
oracle创建函数,存储过程,视图以及for循环语法
oracle自定义函数
posted on 2010-02-24 17:20 艾波 阅读(4527) 评论(2)  编辑  收藏 所属分类: SQL

评论

# re: Oracle 游标总结+整理 2010-02-24 17:52 石头JAVA摆地摊儿
贴主,能讲一讲原理吗?

代码太多没有看懂,

这个是怎么实现的?什么原理?

  回复  更多评论
  

# re: Oracle 游标总结+整理 2010-03-06 23:57 spdia
为什么不用 oracle sequence实现主键  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: