您的位置:首页 > 数据库 > Oracle

oracle存储过程和触发器结合项目详细讲解

2013-11-19 09:28 183 查看
项目名:“学生公寓型床位出租管理系统”

业务逻辑:

情形1:床位出租,就是按床位出租而不是按一个房间来算的,一个房间有固定的床位,有4个6个8个不等的床位,一般这种床位房都是一月一交,也就可以算成30天就得交一次房租。当有租客来入住,我首先要查询有空床位的房间,有空床位就办理入住,就填入租客入住信息。另外租客入住进去后,这个房间的床位就要减少一个,所以需要建立两个表房间表(room)和租客信息表(customer),房间表里有房间号(roomid),房间总床位(sumnum),房间剩余床位(renum),所以房间表就是room(roomid
char, sumnum int, renum int),另外有个问题就是比如房间号908有四个床位,现在都住满人了,在插入一个新租客我就不能让他插入,那该怎么控制呢?后面会讲到

情形2:那入住的时候,租客信息表里应有哪些内容呢?首先毋庸置疑,租客姓名(cname),租客所在房间号(roomid),入住时间(paydate)是必须存在的。仅仅这些行吗?显然这几个字段和room表里的字段仅仅只能完成选择房间,不能完成交费,到期提醒,到期欠交费相关逻辑。

情形3:那么现在就有这样一个问题摆在面前,比如一租客小强已经入住了,怎么记录他已入住的天数(daycounts),这里如果在java程序中可以用线程定义一个定时器,每一天已入住天数就要加1则daycounts=daycounts+1,在oracle中job中也可以实现定时器功能后面会讲到。

情形4:假如小强下个月房租15号到期,一般要在到期前3天提醒交房租,所以现在租客表里就需要一个到期提醒字段,也就是增加一个快到期天数(recounts)来记录,当recounts<=3就要提醒。现在新问题又出现了,快到期了租客就要交房租如果到期后租客没交房租,那应该怎么查出欠交房租的租客了?那么租客表里就要设立一个字段交费状态(paystate)来标明已交和欠交费的用户,如果paystate=0就表示欠交,如果paystate=1就表示已交费,那么在java程序中租客信息展示页面就有个“交费”按钮,一点击交费,paystate就设置为1了。

情形5:想想情形4中有没有问题?问题1:虽然在情形4中点交费只能交一个月的房租,因为情形1中已经说明一般是30天交一次房租,但假如情况特殊,有的租客一下子需要交两个月甚至三个月,那怎么控制。问题2:当小强交了房租后,我点击“交费”paystate=1,下一次他到期后还要交房租,因为现在paystate=1显示他已经交了就不用交了,房东肯定受不了。所以这里30天小强房租再次到期后还要将paystate变为0。问题三:假设小强房租是10月10号到期,因其它原因小强拖欠了一个月房租,一直到11月10号才交了上一个月拖欠的房租,但现在要交房租啊,总要记录,点击交费后,paystate=1显示小强已经交了房租,但实际还拖欠1个月房租,只把10月10号交了,11月10号还没交只是房东要哭了。这里paystate就不能是boolean类型的,paystate就只能为int类型代表月数,1就是交了1月,2就是交了2月,0就是没交,到期后paystate就要减1,比如小强一下交了3个月房租,到期后他没交房租,paystate-1=2,表示他还剩2个月房租,在情形3中天数每天都在自动加1,daycounts=daycounts+1,到期后天数就要归为0,到期的天数就是paystate*30,也就是到期天数=paystate*30时,同时paystate>0才能将daycounts更新为0。程序其实非常简单,主要是搞清业务逻辑。

综合上述两个表设计为:

表设计:

customer(cid()
int, cname(姓名) char, roomid(房间号) char, paydate(入住时间) date, paystate(月数) int, datecounts(入住天数) int, recounts(到期天数) int)租客表

[sql]
view plaincopyprint?

create table CUSTOMER
(
cid VARCHAR2(8) not null,
cname VARCHAR2(6),
roomid NUMBER(6),
paydate DATE,
paystate NUMBER(4),
datecounts NUMBER(5) default 0,

recounts NUMBER(4)
)
tablespace DONCOD
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 16
next 8
minextents 1
maxextents unlimited
);

create table CUSTOMER
(
cid        VARCHAR2(8) not null,
cname      VARCHAR2(6),
roomid     NUMBER(6),
paydate    DATE,
paystate   NUMBER(4),
datecounts NUMBER(5) default 0,
recounts   NUMBER(4)
)
tablespace DONCOD
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 16
next 8
minextents 1
maxextents unlimited
);
room(roomid(房号) char, sumnum(总床位) int, renum(剩余床位) int) 房间表

[sql]
view plaincopyprint?

create table ROOM
(
roomid VARCHAR2(8) not
null,
sumnum NUMBER(6),
renum NUMBER(6)
)
tablespace DONCOD
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 16
next 8
minextents 1
maxextents unlimited
);

create table ROOM
(
roomid VARCHAR2(8) not null,
sumnum NUMBER(6),
renum  NUMBER(6)
)
tablespace DONCOD
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 16
next 8
minextents 1
maxextents unlimited
);


业务解决:

解决情形1中问题,用触发器是最合适的。

[sql]
view plaincopyprint?

create orreplacetrigger CUSINFO
before insert on customer--表示在customer表里创建触发器,并在插入前触发
for each row --表示行级别触发
declare
v_num number; --声明总床位变量

v_count number; --声明对应房间号住的人数

begin
--:new.roomid表示新插入租客的roomid号

select sumnum into v_numfrom roomwhere roomid = :new.roomid;
--给v_count变量赋值
select count(*)into v_countfrom customerwhere
roomid = :new.roomid;
if (v_num - v_count) < 0
then
--解决情形1中不让插入的问题
delete from customerwhere cid = :new.cid;
else
--解决情形1中自动计算剩余床位的问题

update room
set renum =
(v_num - v_count - 1)
where roomid = :new.roomid;
end if;
end CUSINFO;

create or replace trigger CUSINFO
before insert on customer--表示在customer表里创建触发器,并在插入前触发
for each row --表示行级别触发
declare
v_num   number; --声明总床位变量
v_count number; --声明对应房间号住的人数
begin
--:new.roomid表示新插入租客的roomid号
select sumnum into v_num from room where roomid = :new.roomid;
--给v_count变量赋值
select count(*) into v_count from customer where roomid = :new.roomid;
if (v_num - v_count) < 0
then
--解决情形1中不让插入的问题
delete from customer where cid = :new.cid;
else
--解决情形1中自动计算剩余床位的问题
update room
set renum =
(v_num - v_count - 1)
where roomid = :new.roomid;
end if;
end CUSINFO;


解决情形4和情形5用存储过程

[sql]
view plaincopyprint?

create orreplaceprocedure payfei(pid customer.cid%type)as
--声明变量pays,%type表示pays和paystate同类型,相当于paystate包含pays
pays customer.paystate%type;
dat customer.datecounts%type;
rec customer.recounts%type;
begin
--给上面定义的变量赋值
select paystate, datecounts, recounts
into pays, dat, rec

from customer
where cid = pid;--这里防止返回的结果集越界
update customer
--天数加1,主要解决情形5中的问题

set datecounts = datecounts + 1,

recounts = paystate * 30 - datecounts - 1;
if rec = 0 and pays > 0
then
update customer set datecounts = 0, paystate = paystate - 1;
end if;
end;

create or replace procedure payfei(pid customer.cid%type) as
--声明变量pays,%type表示pays和paystate同类型,相当于paystate包含pays
pays customer.paystate%type;
dat  customer.datecounts%type;
rec  customer.recounts%type;
begin
--给上面定义的变量赋值
select paystate, datecounts, recounts
into pays, dat, rec
from customer
where cid = pid;--这里防止返回的结果集越界
update customer
--天数加1,主要解决情形5中的问题
set datecounts = datecounts + 1,
recounts   = paystate * 30 - datecounts - 1;
if rec = 0 and pays > 0
then
update customer set datecounts = 0, paystate = paystate - 1;
end if;
end;


解决情形3定时器的问题

[sql]
view plaincopyprint?

create orreplaceprocedure TIMERas
job number;
begin
--dbms_job是oracle的系统包
dbms_job.submit(JOB => job,
WHAT => 'PAYFEI(1);',--表示要调用的存储过程
next_date => sysdate,
interval => 'sysdate+60/(24*60*60)');--表示一分钟执行一次
--由于一天执行一次效果太慢,所以为了观察方便就搞成一分钟执行一次,下面是一天执行一次
--next_date=>to_date('15-11-2013 15:40:00','dd-mm-yyyy hh24:mi:ss'),
--interval => 'sysdate+1');
commit;
DBMS_JOB.RUN(job);
end TIMER;

create or replace procedure TIMER as
job number;
begin
--dbms_job是oracle的系统包
dbms_job.submit(JOB       => job,
WHAT      => 'PAYFEI(1);',--表示要调用的存储过程
next_date => sysdate,
interval  => 'sysdate+60/(24*60*60)');--表示一分钟执行一次
--由于一天执行一次效果太慢,所以为了观察方便就搞成一分钟执行一次,下面是一天执行一次
--next_date=>to_date('15-11-2013 15:40:00','dd-mm-yyyy hh24:mi:ss'),
--interval => 'sysdate+1');
commit;
DBMS_JOB.RUN(job);
end TIMER;

实际运用:

上述都弄完了,现在在java程序中就变得非常简单。

1:查询空床位,查询剩余2张床位

select * from room where renum>0; select * from room where renum=2;

2:查询还有三天就要到期的租客

select * from customer where recounts=3;

3:查询欠费的租客

select * from customer where paystate<=0;

注意点:上面一个触发器和两个存储过程都要先编译下,测试的话就运行TIMER存储过程就行了

[sql]
view plaincopyprint?

begin timer; commit; end;

begin
timer;
commit;
end;
定时器启动后会一直在后台运行,想关闭定时器先得查询job任务

SELECT * FROM Dba_Jobs
查询出来后记录JOB列所对应的值

然后删除job定时器

[sql]
view plaincopyprint?

begin
dbms_job.remove("查询出来JOB列所对应的值,一般是数字");
commit;
end;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: