数据库事务与并发控制

/*学生信息*/
CREATE TABLE stuInfo(
  SNO INT PRIMARY KEY,
  SName VARCHAR(50) NOT NULL,
  SClass VARCHAR(50)
);

/*校园一卡通信息*/
CREATE TABLE schoolCard(
  CNo  INT PRIMARY KEY,
  SNO  INT NOT NULL,
  cardDate DATETIME,
  remainingSum DECIMAL(7,2) DEFAULT 0 CHECK(remainingSum>=0),
  FOREIGN KEY (SNO) REFERENCES stuInfo(SNO)
);

/*食堂窗口信息*/
CREATE TABLE serviceWindows(
  serviceWNo INT PRIMARY KEY,
  serviceWName NVARCHAR(20) NOT NULL,
  serviceWAdmin NVARCHAR(10),
  serviceWATele CHAR(13)
);

/*食堂就餐消费记录表*/
CREATE TABLE foodConsume(
  CNO        INT,
  serviceWNO INT,
  consumeDT  DATETIME,
  amount     DECIMAL(5,2) CHECK( amount>0),
  PRIMARY KEY (CNO,serviceWNO,consumeDT),
  FOREIGN KEY (cno) REFERENCES schoolCard(CNO),
  FOREIGN KEY (serviceWNO) REFERENCES serviceWindows(serviceWNO)
);

CREATE INDEX idx_foodConsumeAmount ON foodConsume (amount DESC);

ALTER TABLE stuInfo  ADD MobilePhone CHAR(13);

CREATE TABLE WConsume(
  CNO        INT,
  serviceWNO INT,
  num        INT,
  amount     DECIMAL(5,2) CHECK( amount>0),
  PRIMARY KEY(CNO,serviceWNO),
  FOREIGN KEY (cno) REFERENCES schoolCard(CNO),
  FOREIGN KEY (serviceWNO) REFERENCES serviceWindows(serviceWNO)
);

/*简易测试数据,运行到系统中,方便测试*/
INSERT INTO stuinfo(sno,sname,sclass) VALUES(20001,'张三','软件工程201');
INSERT INTO stuinfo(sno,sname,sclass) VALUES(20002,'李四','软件工程202');
INSERT INTO stuinfo(sno,sname,sclass) VALUES(20003,'王五','软件工程203');
INSERT INTO stuinfo(sno,sname,sclass) VALUES(20004,'赵六','软件工程201');
INSERT INTO stuinfo(sno,sname,sclass) VALUES(20005,'钱七','软件工程202');
INSERT INTO schoolCard(cno,sno,remainingSum) VALUES(20001,20001,100);
INSERT INTO schoolCard(cno,sno,remainingSum) VALUES(20002,20002,300);
INSERT INTO schoolCard(cno,sno,remainingSum) VALUES(20003,20003,300);
INSERT INTO schoolCard(cno,sno,remainingSum) VALUES(20004,20004,300);
INSERT INTO schoolCard(cno,sno,remainingSum) VALUES(20005,20005,300);
INSERT INTO serviceWindows(serviceWno,ServiceWname) VALUES(1,'1号窗口');
INSERT INTO serviceWindows(serviceWno,ServiceWname) VALUES(2,'2号窗口');
INSERT INTO serviceWindows(serviceWno,ServiceWname) VALUES(3,'3号窗口');
INSERT INTO serviceWindows(serviceWno,ServiceWname) VALUES(4,'4号窗口');
INSERT INTO WConsume(cno,serviceWno,num,amount) VALUES(20001,1,10,200);

DROP PROCEDURE IF EXISTS p_wconsume;
DELIMITER @@
CREATE PROCEDURE p_wconsume(
  _cno INT,
  _serviceWno INT,
  _amount DECIMAL(5,2)
)
BEGIN
  DECLARE iremainingSum INT;
  DECLARE EXIT HANDLER FOR SQLEXCEPTION /*遗漏错误处理扣15*/
  BEGIN
    signal SQLSTATE '45000' SET message_text='消费不成功';
    ROLLBACK; /*遗漏rollback扣5分,看错误处理是exit还是continue,rollback位置不同*/
  END;
/*处理余额是否足够,更严谨的做法是增加判断iremainingSum是否为空 (10分)*/
SET iremainingSum = (SELECT remainingSum FROM schoolcard WHERE cno=_cno);
IF _amount > iremainingSum THEN
          signal SQLSTATE '45000' SET message_text='余额不足'; #可以用其他方式处理错误信息
END IF;
/*处理事务(事务语句10分,包括后面的commit)*/
START TRANSACTION;
UPDATE schoolcard SET remainingSum=remainingSum-_amount WHERE cno=_cno; /*不正确扣10分*/
        IF EXISTS(SELECT * FROM WConsume WHERE cno=_cno AND servicewno=_serviceWno) THEN /*可用其他判断方式,缺少扣10分*/
  /*如果卡在当前窗口已有消费记录,执行update操作,20分*/
          UPDATE WConsume SET num=num+1,amount=amount+_amount WHERE cno=_cno AND servicewno=_serviceWno;
ELSE
  /*如果卡在当前窗口没有消费记录,则执行insert操作,20分*/
  INSERT INTO WConsume(cno,serviceWno,num,amount) VALUES(_cno,_serviceWno,1,_amount);
END IF;
COMMIT;
END@@
DELIMITER ;

call p_wconsume(20010,1,20); #是否能捕获到错误?

call p_wconsume(20001,2,30); #运行2次,每次运行后查看schoolcard、WConsume

call p_wconsume(20001,1,30); #运行2次,每次运行后查看schoolcard、WConsume

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值