xzh2000的博客 本人提供杭州地区Oracle现场技术支持服务,包括性能调整、DataGuard、RAC等。
24 08, 2006
函数为什么不允许dml操作
作者 xzh2000 15:09 | Permalink 静态链接网址 | Comments 最新回复 (0) | Trackback 引用 (0) | 技术交流

前天开发工程师要求产生一个有规则的唯一编码,编码的格式是yyyymmddx#5,即最后五位每天都是从1开始计数,他们自己的实现方式是直接对表求max(id),如果前8位是当前日期,则编码后六位加1,否则产生编码的规则是:前8位是当前日期,后六位从1开始计数。。。


由于这种编码规则的需求比较多,如果没有一个很好的接口,那开发工程师每次都要为新的需求(新的表)进行编码,代码重用性差,所以决定写个产生编码的函数来实现这种需求,这需要在数据库中创建一个表来保存一些需要持久化的信息,比如当前的序列及日期,虽然效率没有sequence高,但由于数据量也不大,所以还是可以满足需求的。

表结构:test(code_name,curr_val,curr_date,code_comment);

create or replace function get_serviceid(v_serviceid in varchar2) is
v_code varchar2(32);
v_curr_val sys_admin_code.curr_val%type;
v_curr_date sys_admin_code.curr_date%type;
begin
--取编码的基本信息
select curr_val, curr_date into v_curr_val, v_curr_date
from sys_admin_code
where code_name = v_serviceid for update;

--如果是当天,则只更新curr_val
if trunc(v_curr_date,'dd') = trunc(sysdate,'dd') then
--产生当前的编码
v_code := to_char(v_curr_date,'yyyymmdd') || v_serviceid || lpad(v_curr_val,5,'0');

--编码累加
update sys_admin_code
set curr_val = v_curr_val + 1
where code_name = v_serviceid;
elsif trunc(v_curr_date,'dd') < trunc(sysdate,'dd') then
--编码置1
v_code := to_char(sysdate,'yyyymmdd') || v_serviceid || lpad(1,5,'0');

--如果是第二天,则需要更新curr_date与curr_val
update sys_admin_code
set curr_val = 2, curr_date = sysdate
where code_name = v_serviceid;
end if;

--提交数据累计
commit;

return v_code;
end;

在创建函数是没有问题的,但在执行函数时报错:

15:52:01 SQL> select get_serviceid('1') from dual;
select get_serviceid('1') from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "TAOBAO.GET_SERVICEID", line 12

但在pl/sql developer上是可以调试该函数的,结果也正确,但用select get_serviceid('1') from dual;却报错,在函数内部改成用动态sql也不成,最后只有改成存储过程了事,如果一定要用函数,也有办法的,在函数中使用匿名事务,测试脚本如下所示:

create table test(id number,cnt number);
insert into test values(1,1);

create or replace function uf_get_current return number
as
pragma autonomous_transaction;

v_cnt number;
begin
select max(cnt) into v_cnt
from test;

update test set cnt = cnt + 1;
commit;

return v_cnt;
end;
/
Function created.

20:53:37 SQL> select uf_get_current from dual;
UF_GET_CURRENT
--------------
1
20:53:44 SQL> select uf_get_current from dual;
UF_GET_CURRENT
--------------
2
20:53:46 SQL> select uf_get_current from dual;
UF_GET_CURRENT
--------------
3

20:53:48 SQL> create or replace function uf_get_current return number
20:54:03 2 as
20:54:03 3 --pragma autonomous_transaction;
20:54:03 4
20:54:03 5 v_cnt number;
20:54:03 6 begin
20:54:03 7 select max(cnt) into v_cnt
20:54:03 8 from test;
20:54:03 9
20:54:03 10 update test set cnt = cnt + 1;
20:54:03 11 commit;
20:54:03 12
20:54:03 13 return v_cnt;
20:54:03 14 end;
20:54:04 15 /

Function created.

20:54:04 SQL> select uf_get_current from dual;
select uf_get_current from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "TAOBAO.UF_GET_CURRENT", line 10

Comments
博客日历
« 八月 2008 »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
搜索
最新发表
文章分类
文章归档
网站链接
新闻聚合