Oracle校园卡消费系统(文档+代码)

系统分析


题目要求

校园卡可以用于食堂就餐和商铺消费结算。每个食堂消费终端内置有唯一的终端编号,结算时先输入金额,顾客再刷卡,系统自动从卡中扣除相应金额。每个商铺销售终端都有唯一编号,消费时需要输入各商品编号、数量,系统从数据库中读出单价并计算消费总额。每周由财务处进行一次集中结算,统计每个商铺和食堂窗口的销售额。每个食堂窗口和商铺均有多个消费结算终端,均连接到数据库服务器。客户需要先到财务处预存款才可消费。

题目分析

通过对上述题目要求的分析,总结出一个完整的校园卡消费系统需要具备:存现、转账、退款、商铺消费、窗口消费、挂失、学生信息查询、学生所有订单查询、终端消费统计等功能。

在对现实生活中校园卡消费终端的研究发现,每个终端都内置一个终端编号,每张校园卡也内置一个校园卡卡号(不是学号),比如在消费、存现的过程中,们只需要输入金额,在卡片接触终端机器时,系统会自动识别两端的编号。但在我们模拟过程中,做不到自动识别,所以需要手动的输入终端编号和校园卡卡号,然后通过卡号去查询所属学号,产生一个与学号相关的订单记录并插入到总数据库中。这样即便是更换新的校园卡时,仍然可以查询到消费记录。

但挂失就不一样了,挂失是在我们不知道卡号的情况下,需要用学号到办理中心进行卡号变更,所以输入的是学号。

数据库设计与实现


概念结构设计

表结构:

校园卡信息表card:
校园卡信息表card
终端信息表terminal:
终端信息表terminal
商品信息表goods:
商品信息表goods:
消费记录表consume:
消费记录表consume
变更记录表change:
变更记录表change
E-R图:
E-R图

物理结构设计

数据库的物理设计就是根据所选用的DBMS和处理需求,进行物理存储安排,建立索引,形成数据库的内模式, 为逻辑数据模型选取一个最适合应用要求的物理结构的过程, 在这个阶段中要完成两大任务:

  1. 确定数据库的物理结构,在关系数据库中主要是存取方法和存储结构;
  2. 对物理结构进行评价,评价的重点是时间和空间效率。

为数据库中各基本表建立的索引如下:

由于基本表 card的属性user_id经常在查询条件和连接操作的连接条件中出现,考虑在这个属性上建立索引。

数据库实施


建立数据表、索引、序列,视图

建立5个数据表,索引为校园卡信息表中的学号属性,并分别在校园卡卡号,消费订单编号,业务办理编号上建立序列。当办理挂失业务和产生订单时,自动生成新的唯一编号。视图为财务部统计的各终端消费汇总情况,按消费金额排名。

插入初始数据

录入终端信息,校园卡信息,以及商品信息,订单信息由存储过程自动生成。

设计pl存储过程

1. 商铺消费 EXECUTE sp_sql(卡号,终端编号,商品编号,商品数量);

输入校园卡编号,终端编号,商品编号,商品数量,订单号由序列自动创建、时间为当前系统时间。首先判断是否挂失,如果挂失则输出“已冻结”,如图
在这里插入图片描述
否则根据商品编号查找商品表goods获取商品价格,乘以数量计算订单金额,查询用户余额,如果余额不足则输出
在这里插入图片描述
满足即可创建订单,查寻校园卡信息表获取用户学号,插入一条新的消费记录,更改校园卡余额,获取用户姓名。根据终端编号查询终端名,最后由程序输出本次的记录,类似于小票
在这里插入图片描述
整个过程将4个表联系在了一起,需要注意的是创建的订单里面是学号,不是卡号便于后期查询个人消费记录。

2. 食堂消费 EXECUTE st_sql(卡号,终端编号,金额);

同上,只是没有商品数量字段,产生的订单格式一样。
在这里插入图片描述

3. 存现程序 EXECUTE cx_sql(卡号,金额);

输入卡号、存现金额。首先查询校园卡信息表判断是否挂失,挂失则输出校园卡已冻结,否则更改余额,并产生业务办理记录,如(交易单号、根据卡号查询的学号、默认的业务类型“存现”、存现金额、自动生成的交易时间),交易单号由序列生成。然后输出业务办理的小票
在这里插入图片描述

4. 退款程序 EXECUTE tk_sql(卡号);

输入卡号、首先判断是否挂失,如果未挂失则将校园卡余额更新为0,产生并插入一条业务办理记录,如(交易单号、根据卡号查询的学号、默认的业务类型“退款”、退款金额、自动生成的交易时间)。最后输出业务业务办理的小票
在这里插入图片描述

5. 转账程序 EXECUTE zz_sql(卡号1,卡号2,金额);

输入卡号1,卡号2,转账金额。首先判断两个卡号是否挂失,然后判断转出账户金额是否充足,若条件都满足则扣减卡号1账户余额,增添卡号2账户余额,并产生业务办理记录(交易单号、根据卡号1查询的学号、默认的业务类型“转账”、转账金额、自动生成的交易时间)。最后打印小票
在这里插入图片描述

6. 挂失程序 EXECUTE gs_sql(学号);

输入学号,查询到当前使用的校园卡卡号、为其添加“挂失”字段,然后创建新的校园卡表记录,将学号、名字、金额转移(原金额设为0),挂失字段为空。
在这里插入图片描述

7. 查询某个账户信息 EXECUTE user_sql(学号);

输入学号,查询校园卡信息表,获得当前正在使用的卡号、姓名、余额,并打印输出
在这里插入图片描述

8. 查询某个用户所有订单信息 EXECUTE user_order(学号,:data); PRINT data;

输入学号,查询消费记录表,获取金额信息,将类型设置为“消费”,根据存储的终端编号查找终端信息表获得终端名,获得订单创建的时间戳。在根据学号查询变更记录表,获取金额信息,将类型设置为存储的类型{“存现”,“转账”,“退款”},地点字段为空,获得记录创建时间。然后将两个查询的结果表纵向连接,按时间先后排序,输出的一张表,这里用到了游标的知识,表的属性为(金额、类型(存现、转账、退款、消费)、地点(只有消费显示地点)、具体时间),结果如下
在这里插入图片描述

9. 财务部查看各终端消费视图 select * from v_rank;

按消费金额降序排序,统计各个终端的消费情况,比如输出的字段(终端名、所在区域、累计消费金额)。
在这里插入图片描述

其它说明


1. PL程序中序列的引用问题

最开始采用p_change_id:=change_seq.nextval这种形式引用,在oracle11版本上测试成功,但在oracle10版本上出错,最后使用了课本上规范的写法。

SELECT change_seq.nextval
INTO p_change_id
FROM dual;

2. PL程序中多条件查询要使用括号

要根据学号查询校园卡信息表中正在使用的校园卡,一开始没有在and后面的条件外加括号,使得每次查询都出错,但过程却创建成功,网上找了很多资料也不知道什么原因,最后无意中尝试用括号括起来,成功了。

WHERE user_id=p_user_id AND (card_lost is null);

3. 如何输入一个参数,经查询返回一个表

一开始考虑到两个方案,一是实现能传入参数的视图,因为视图能输出表,但不能引入参数,没有找到这样应用的实例;二使用存储过程,可以传参但只能一行一行的输出,虽然使用for loop循环,但破坏了表的输出形式,经查询,网上都是使用包和游标,太复杂了就没有采用。后来随意尝试发现不用包只用游标也可以,用游标记录一个结果集,通过out传参。
存储过程内定义:data out sys_refcursor;
过程外定义变量:VARIABLE data refcursor;
打印输出:print data;
有关此问题看:oracle PL/SQL存储过程的使用,以及如何输入参数,经处理输出一张表sqlplus

代码


drop table card;
drop table terminal;
drop table goods;
drop table consume;
drop table change;
DROP SEQUENCE card_seq;
DROP SEQUENCE consume_seq;
DROP SEQUENCE change_seq;
drop procedure gs_sql;
drop procedure cx_sql;
drop procedure tk_sql;
drop procedure zz_sql;
drop procedure st_sql;
drop procedure sp_sql;
drop procedure user_sql;
drop procedure user_order;
drop view v_rank;
 
--建表
create table card(
card_id varchar2(10) primary key,
user_id varchar2(10) not null,
user_name varchar2(10) not null,
card_account float not null,
card_lost varchar2(10)check(card_lost='挂失') 
);
 
create table terminal(
terminal_id varchar2(10) primary key,
terminal_name varchar2(15) not null,
terminal_area varchar2(10)check(terminal_area='西区'or terminal_area='南区') not null
);
 
create table goods(
goods_id varchar2(10) primary key,
goods_name varchar2(10) not null,
goods_money float not null
);
 
create table consume(
consume_id varchar2(10) primary key,
terminal_id varchar2(10) not null,
user_id varchar2(10) not null,
consume_money float not null,
consume_time date not null
);
 
create table change(
change_id varchar2(10) primary key,
user_id varchar2(10) not null,
change_type varchar2(10)check(change_type='存现'or change_type='退款'or change_type='转账') not null,
change_money float not null,
change_time date not null
);
 
--索引
CREATE INDEX i_user_id ON card(user_id);
 
--序列
CREATE SEQUENCE card_seq
INCREMENT BY 1
START WITH 10000
CACHE 500
NOCYCLE;
 
CREATE SEQUENCE consume_seq
INCREMENT BY 1
START WITH 20000000
CACHE 500
NOCYCLE;
 
CREATE SEQUENCE change_seq
INCREMENT BY 1
START WITH 30000000
CACHE 500
NOCYCLE;
 
--插入数据
INSERT INTO card VALUES(card_seq.nextval,'1704011011','张三',100,null);
INSERT INTO card VALUES(card_seq.nextval,'1704011012','李四',1000,null);
INSERT INTO card VALUES(card_seq.nextval,'1704011021','王五',0,'挂失');
INSERT INTO card VALUES(card_seq.nextval,'1704011021','王五',100,null);
 
INSERT INTO goods VALUES('1','矿泉水',2);
INSERT INTO goods VALUES('2','面包',6);
INSERT INTO goods VALUES('3','饼干',4);
INSERT INTO goods VALUES('4','方便面',5);
INSERT INTO goods VALUES('5','卫生纸',3);
INSERT INTO goods VALUES('6','烤肠',1);
INSERT INTO goods VALUES('7','水壶',55);
 
INSERT INTO terminal VALUES('100','1食堂商店','西区');
INSERT INTO terminal VALUES('101','1食堂1-1班组','西区');
INSERT INTO terminal VALUES('102','1食堂1-2班组','西区');
INSERT INTO terminal VALUES('103','2食堂商店','西区');
INSERT INTO terminal VALUES('104','2食堂2-1班组','南区');
 
--存现pl
SET serveroutput ON
SET verify OFF
CREATE OR REPLACE PROCEDURE cx_sql
  (p_card_id IN card.card_id%TYPE,
    p_money IN card.card_account%TYPE)
IS
    p_user_id card.user_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_lost card.card_lost%TYPE;
    p_change_id change.change_id%TYPE;
  BEGIN
  SELECT user_id,user_name,card_lost
  INTO p_user_id,p_user_name,p_card_lost
  FROM card
  WHERE card_id=p_card_id;
  IF p_card_lost='挂失' THEN
     DBMS_OUTPUT.PUT_LINE('存现失败,校园卡已冻结!');
  ELSE
    BEGIN
      UPDATE card SET card_account=card_account+p_money
      WHERE card_id=p_card_id;
      SELECT change_seq.nextval
      INTO p_change_id
      FROM dual;
      INSERT INTO change VALUES(p_change_id,p_user_id,'存现',p_money,SYSDATE);
      DBMS_OUTPUT.PUT_LINE('账户:'||p_user_id||p_user_name||' 存现成功,金额'||p_money||'元,交易编号:'||p_change_id);
    END;
  END IF;
END;
/
 
--退款pl
CREATE OR REPLACE PROCEDURE tk_sql
  (p_card_id IN card.card_id%TYPE)
IS
    p_user_id card.user_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_account card.card_account%TYPE;
    p_card_lost card.card_lost%TYPE;
    p_change_id change.change_id%TYPE;
  BEGIN
  SELECT user_id,user_name,card_account,card_lost
  INTO p_user_id,p_user_name,p_card_account,p_card_lost
  FROM card
  WHERE card_id=p_card_id;
  IF p_card_lost='挂失' THEN
     DBMS_OUTPUT.PUT_LINE('退款失败,校园卡已冻结!');
  ELSE
    BEGIN
      UPDATE card SET card_account=0
      WHERE card_id=p_card_id;
      SELECT change_seq.nextval
      INTO p_change_id
      FROM dual;
      INSERT INTO change VALUES(p_change_id,p_user_id,'退款',p_card_account,SYSDATE);
      DBMS_OUTPUT.PUT_LINE('账户:'||p_user_id||p_user_name||' 退款成功,金额'||p_card_account||'元,交易编号:'||p_change_id);
    END;
  END IF;
END;
/
 
--转账pl
CREATE OR REPLACE PROCEDURE zz_sql
  ( p_card_id1 IN card.card_id%TYPE,
    p_card_id2 IN card.card_id%TYPE,
    p_money IN card.card_account%TYPE)
IS
    p_user_id1 card.user_id%TYPE;
    p_user_name1 card.user_name%TYPE;
    p_card_lost1 card.card_lost%TYPE;
    p_user_id2 card.user_id%TYPE;
    p_user_name2 card.user_name%TYPE;
    p_card_lost2 card.card_lost%TYPE;
    p_change_id change.change_id%TYPE;
    p_card_account card.card_account%TYPE;
  BEGIN
  SELECT user_id,user_name,card_account,card_lost
  INTO p_user_id1,p_user_name1,p_card_account,p_card_lost1
  FROM card
  WHERE card_id=p_card_id1;
  SELECT user_id,user_name,card_lost
  INTO p_user_id2,p_user_name2,p_card_lost2
  FROM card
  WHERE card_id=p_card_id2;
  IF p_card_lost1='挂失' THEN
    DBMS_OUTPUT.PUT_LINE('转账失败,'||p_card_id1||'已冻结!');
  ELSIF p_card_lost2='挂失' THEN
    DBMS_OUTPUT.PUT_LINE('转账失败,'||p_card_id2||'已冻结!');
  ELSE
    BEGIN
    IF p_card_account<p_money THEN
      DBMS_OUTPUT.PUT_LINE('转账失败,校园卡余额不足!');
      ELSE
      BEGIN
      UPDATE card SET card_account=card_account-p_money
      WHERE card_id=p_card_id1;
      UPDATE card SET card_account=card_account+p_money
      WHERE card_id=p_card_id2;
      SELECT change_seq.nextval
      INTO p_change_id
      FROM dual;
      INSERT INTO change VALUES(p_change_id,p_user_id1,'转账',p_money,SYSDATE);
      DBMS_OUTPUT.PUT_LINE('账户:'||p_user_id1||p_user_name1||' 向账户:'||p_user_id2||p_user_name2||' 转账金额'||p_money||'元,交易编号:'||p_change_id);
      END;
    END IF;
  END;
END IF;
END;
/
 
--挂失pl
CREATE OR REPLACE PROCEDURE gs_sql
  (p_user_id IN card.user_id%TYPE)
IS
    p_card_id card.card_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_account card.card_account%TYPE;
  BEGIN
  SELECT card_id,user_name,card_account
  INTO p_card_id,p_user_name,p_card_account
  FROM card
  --出现的问题,and后面的条件要用括号括起来
  WHERE user_id=p_user_id AND (card_lost is null);
  UPDATE card SET card_lost='挂失',card_account=0
  WHERE card_id=p_card_id;
  SELECT card_seq.nextval
  INTO p_card_id
  FROM dual;
  INSERT INTO card VALUES(p_card_id,p_user_id,p_user_name,p_card_account,null);
  DBMS_OUTPUT.PUT_LINE(p_user_id||'已成功办理挂失业务,您的新卡号:'||p_card_id);
END;
/
 
--商铺消费pl
CREATE OR REPLACE PROCEDURE sp_sql
  ( p_card_id IN card.card_id%TYPE,
  	p_terminal_id IN terminal.terminal_id%TYPE,
  	p_goods_id IN goods.goods_id%TYPE,
    p_num IN INT
    )
IS
    p_user_id card.user_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_account card.card_account%TYPE;
    p_card_lost card.card_lost%TYPE;
    p_goods_name goods.goods_name%TYPE;
    p_goods_money goods.goods_money%TYPE;
    p_remain FLOAT;
    p_money FLOAT;
    p_terminal_name terminal.terminal_name%TYPE;
    p_terminal_area terminal.terminal_area%TYPE;
    p_consume_id consume.consume_id%TYPE;
  BEGIN
  SELECT user_id,user_name,card_account,card_lost
  INTO p_user_id,p_user_name,p_card_account,p_card_lost
  FROM card
  WHERE card_id=p_card_id;
  IF p_card_lost='挂失' THEN
     DBMS_OUTPUT.PUT_LINE('无法消费,校园卡已冻结!');
  ELSE
    BEGIN
    SELECT goods_name,goods_money
    INTO p_goods_name,p_goods_money
    FROM goods
    WHERE goods_id=p_goods_id;
    p_money:=p_goods_money*p_num;
    IF p_card_account<p_money THEN
      DBMS_OUTPUT.PUT_LINE('校园卡余额不足!');
    ELSE
    BEGIN
      p_remain:=p_card_account-p_money;
      UPDATE card SET card_account=p_remain
      WHERE card_id=p_card_id;
      SELECT consume_seq.nextval
      INTO p_consume_id
      FROM dual;
      INSERT INTO consume VALUES(p_consume_id,p_terminal_id,p_user_id,p_money,SYSDATE);
      SELECT terminal_name,terminal_area
      INTO p_terminal_name,p_terminal_area
      FROM terminal
      WHERE terminal_id=p_terminal_id;
      DBMS_OUTPUT.PUT_LINE(p_user_name||' 学号:'||p_user_id||' 在'||p_terminal_area||p_terminal_name||' 购买 '||p_goods_name||p_num||'个,消费'||p_money||'元,余额'||p_remain||'元,时间'||SYSDATE||'订单编号'||p_consume_id);
      END;
      END IF;
    END;
  END IF;
END;
/
 
--食堂消费pl
CREATE OR REPLACE PROCEDURE st_sql
  ( p_card_id IN card.card_id%TYPE,
  	p_terminal_id IN terminal.terminal_id%TYPE,
  	p_money IN FLOAT
    )
IS
    p_user_id card.user_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_account card.card_account%TYPE;
    p_card_lost card.card_lost%TYPE;
    p_remain FLOAT;
    p_terminal_name terminal.terminal_name%TYPE;
    p_terminal_area terminal.terminal_area%TYPE;
    p_consume_id consume.consume_id%TYPE;
  BEGIN
  SELECT user_id,user_name,card_account,card_lost
  INTO p_user_id,p_user_name,p_card_account,p_card_lost
  FROM card
  WHERE card_id=p_card_id;
  IF p_card_lost='挂失' THEN
     DBMS_OUTPUT.PUT_LINE('无法消费,校园卡已冻结!');
  ELSE
    BEGIN
    IF p_card_account<p_money THEN
      DBMS_OUTPUT.PUT_LINE('校园卡余额不足!');
    ELSE
    BEGIN
      p_remain:=p_card_account-p_money;
      UPDATE card SET card_account=p_remain
      WHERE card_id=p_card_id;
      SELECT consume_seq.nextval
      INTO p_consume_id
      FROM dual;
      INSERT INTO consume VALUES(p_consume_id,p_terminal_id,p_user_id,p_money,SYSDATE);
      SELECT terminal_name,terminal_area
      INTO p_terminal_name,p_terminal_area
      FROM terminal
      WHERE terminal_id=p_terminal_id;
      DBMS_OUTPUT.PUT_LINE(p_user_name||' 学号:'||p_user_id||' 在'||p_terminal_area||p_terminal_name||' 消费'||p_money||'元,余额'||p_remain||'元,时间'||SYSDATE||'订单编号'||p_consume_id);
      END;
      END IF;
    END;
  END IF;
END;
/
 
--查询某一学生信息
CREATE OR REPLACE PROCEDURE user_sql
  (p_user_id IN card.user_id%TYPE) 
  IS
    p_card_id card.card_id%TYPE;
    p_user_name card.user_name%TYPE;
    p_card_account card.card_account%TYPE;
BEGIN
  SELECT card_id,user_name,card_account
  INTO p_card_id,p_user_name,p_card_account
  FROM card
  WHERE user_id=p_user_id AND (card_lost is null);
  dbms_output.put_line(p_card_id||' '||p_user_id||' '||p_user_name||' '||p_card_account);
exception
  WHEN no_data_found THEN
    dbms_output.put_line('then user no exists');
END;
/
 
--查询某个学生的所有账单
create or replace procedure user_order
  (p_user_id IN card.user_id%TYPE,
    data out sys_refcursor) 
  is
begin
  open data for select * from(
  select consume_money as 金额,
  '消费' as 类型,
  (select terminal_name from terminal where terminal_id=c.terminal_id) as 地点,
  to_char(consume_time,'YYYY-MM-DD HH24:MI:SS')时间 
  from consume c 
  where user_id=p_user_id 
union 
select change_money as 金额,
change_type as 类型,
null as 地点,
to_char(change_time,'YYYY-MM-DD HH24:MI:SS')时间 
from change 
where user_id=p_user_id) order by 时间;
end;
/
VARIABLE data refcursor;
 
--创建视图
create or replace view v_rank as
select t.terminal_name as 消费终端,t.terminal_area as 所在校区,sum(consume_money) as 累计消费
from consume c inner join terminal t on c.terminal_id = t.terminal_id 
group by t.terminal_name,t.terminal_area order by 累计消费 desc;
 
--操作pl程序
EXECUTE cx_sql('10001',200);
EXECUTE sp_sql('10004','100','1',2);
EXECUTE st_sql('10001','101',13.8);
EXECUTE sp_sql('10001','100','4',1);
EXECUTE zz_sql('10004','10001',20);
EXECUTE st_sql('10002','104',34);
EXECUTE cx_sql('10004',250);
EXECUTE st_sql('10001','102',6);
EXECUTE sp_sql('10001','100','6',4);
EXECUTE sp_sql('10002','103','7',1);
EXECUTE gs_sql('1704011011');
EXECUTE sp_sql('10004','103','2',1);
EXECUTE sp_sql('10001','103','4',2);
EXECUTE cx_sql('10005',50);
EXECUTE st_sql('10002','102',16);
EXECUTE tk_sql('10005');
EXECUTE st_sql('10005','101',11);
EXECUTE user_sql('1704011011');
EXECUTE user_order('1704011011',:data)
PRINT data;
 
select * from card;
select * from terminal;
select * from goods;
select * from consume;
select * from change;
select * from v_rank;
  • 6
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值