Oracle校園卡消費系統(文檔+代碼)

目錄

 

1 系統分析

2 數據庫設計與實現

2.1 概念結構設計

2.3 物理結構設計

3 數據庫實施

4 其它說明


1 系統分析

    1. 題目要求

校園卡可以用於食堂就餐和商鋪消費結算。每個食堂消費終端內置有唯一的終端編號,結算時先輸入金額,顧客再刷卡,系統自動從卡中扣除相應金額。每個商鋪銷售終端都有唯一編號,消費時需要輸入各商品編號、數量,系統從數據庫中讀出單價並計算消費總額。每週由財務處進行一次集中結算,統計每個商鋪和食堂窗口的銷售額。每個食堂窗口和商鋪均有多個消費結算終端,均連接到數據庫服務器。客戶需要先到財務處預存款纔可消費。

    1. 題目分析

通過對上述題目要求的分析,總結出一個完整的校園卡消費系統需要具備:存現、轉賬、退款、商鋪消費、窗口消費、掛失、學生信息查詢、學生所有訂單查詢、終端消費統計等功能。

在對現實生活中校園卡消費終端的研究發現,每個終端都內置一個終端編號,每張校園卡也內置一個校園卡卡號(不是學號),比如在消費、存現的過程中,們只需要輸入金額,在卡片接觸終端機器時,系統會自動識別兩端的編號。但在我們模擬過程中,做不到自動識別,所以需要手動的輸入終端編號和校園卡卡號,然後通過卡號去查詢所屬學號,產生一個與學號相關的訂單記錄並插入到總數據庫中。這樣即便是更換新的校園卡時,仍然可以查詢到消費記錄。

但掛失就不一樣了,掛失是在我們不知道卡號的情況下,需要用學號到辦理中心進行卡號變更,所以輸入的是學號。

2 數據庫設計與實現

2.1 概念結構設計

表結構:

校園卡信息表card:

終端信息表terminal:

商品信息表goods:

消費記錄表consume:

變更記錄表change:

E-R圖:

2.3 物理結構設計

 

數據庫的物理設計就是根據所選用的DBMS和處理需求,進行物理存儲安排,建立索引,形成數據庫的內模式, 爲邏輯數據模型選取一個最適合應用要求的物理結構的過程, 在這個階段中要完成兩大任務:

1)確定數據庫的物理結構,在關係數據庫中主要是存取方法和存儲結構;

2)對物理結構進行評價,評價的重點是時間和空間效率。

爲數據庫中各基本表建立的索引如下:

由於基本表 card的屬性user_id經常在查詢條件和連接操作的連接條件中出現,考慮在這個屬性上建立索引。

3 數據庫實施

  • 建立數據表、索引、序列,視圖

建立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;

按消費金額降序排序,統計各個終端的消費情況,比如輸出的字段(終端名、所在區域、累計消費金額)。

 

4 其它說明

遇到的問題

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;

代碼:


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;



 

發佈了9 篇原創文章 · 獲贊 2 · 訪問量 416
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章