首先先来一道小题
希尔顿是一跨国旅馆经营公司,其数据库系统中有如下4个表:
Hotel (hotelNo, name, city)
Room (roomNo, hotelNo, type, price)
Booking (hotelNo, guestId, dateFrom, dateTo, roomNo)
Guest (guestId, name, guestcity, creditcard)
使用SQL的DDL写出Booking表的定义,要求至少包含一条业务规则约束;
DDL全称是data,definition,language,数据库设计语言,包含CREATE,DROP,ALTER等等重要关键字,这题我理解的是用Sql创建一个booking表
新建规则包括:
1.房间号定为101-999,百位表示楼层,个位和十位表示房间,假定希尔顿酒店只有九楼
2。订房日期必须大于当前日期,到期时间必须大于订房日期
3…所有的外键都设置为级联更新和删除,这样当外键所在表有改变时,可以及时更新
建表语句为
CREATE TABLE Booking(
hotelNo INT NOT NULL,
guestId INT NOT NULL,
dateFrom DATE NOT NULL CHECK (dateFrom>CURRENT_DATE),
dateTo DATE NOT NULL CHECK (dateTo>dateFrom),
roomNo INT NOT NULL CHECK (roomNo>100 AND roomNo<1000),
PRIMARY KEY(hotelNo,guestId ,dateFrom),
FOREIGN KEY(hotelNo) REFERENCES hotel(hotelNo) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(guestId ) REFERENCES guest(guestId ) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(roomNo ) REFERENCES room(roomNo ) ON DELETE CASCADE ON UPDATE CASCADE
);
由于之前建好的表guest的主键是guestno,并且已经有了booking表,为了实现建表成功,所以某些细节小改了一下
以下是书中题目
7.11
这道题考察了SQL完整性增强特性,包括非空数据,域约束,实体完整性,引用完整性,一般性约束
为了保证能创建表,所以重新创建了数据库和hotel表
Room表
CREATE TABLE Room(
roomNo INT NOT NULL CHECK (roomNo>=1 AND roomNo<=100),
hotelNo INT NOT NULL,
type VARCHAR(6) NOT NULL CHECK (type IN ("Single","Double","Family")),
price INT NOT NULL CHECK (price>=10 AND price<=100),
PRIMARY KEY(roomNo,hotelNo),
FOREIGN KEY (hotelNo) REFERENCES hotel(hotelNo)
);
按照创建顺序,应该Guest表优先创建,比较简单,就不截图了
Guest
CREATE TABLE Guest(
guestNo INT,
guestName VARCHAR(10) NOT NULL,
guestAddress VARCHAR(10) NOT NULL,
PRIMARY KEY(guestNo)
);
Booking
CHECK语句可以实现字段内容筛选,直接加在字段声明的后面
使用CONSTRAINT约束实现相同房间不能订两次和客人不能重复预订的功能
此外,EXISTS是有"S"的,别记错了!!!(微笑)
CREATE TABLE Booking(
hotelNo INT NOT NULL,
dateFrom DATE CHECK (dateFrom>CURRENT_DATE),
dateTo DATE CHECK (dateTo>CURRENT_DATE),
roomNo INT NOT NULL,
guestNo INT NOT NULL,
PRIMARY KEY(hotelNo,dateFrom,guestNo),
FOREIGN KEY(hotelNo) REFERENCES hotel(hotelNo) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(roomNo) REFERENCES room(roomNo) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(guestNo) REFERENCES guest(guestNo)ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT roombook CHECK (NOT EXISTS (SELECT * FROM Booking b
WHERE Booking.hotelNo=b.hotelNo AND Booking.dateTo >b.dateFrom
AND Booking.dateFrom<b.dateTo AND Booking.roomNo=b.roomNo)),
CONSTRAINT guestbook CHECK (NOT EXISTS (SELECT *FROM Booking b
WHERE Booking.hotelNo=b.hotelNo AND Booking.guestNo=b.guestNo
AND Booking.dateFrom<b.dateTo AND Booking.dateTo>b.dateFrom))
);
8.11
触发器顾名思义,在对数据进行操作的时候,如果满足了触发器的条件,那么将会触发器会自动执行
一般形式为:
CREATE TRIGGER trigger_name
BEFORE | AFTER|INSTEAD OF
INSERT | DELETE | UPDATE [OF 触发列表]
ON table_name
……
(a)
语句解释:
RAISE_APPLICATION_ERROR 是将应用程序专有的错误从服务器端转达到客户端应用程序(其他机器上的SQLPLUS或者其他前台开发语言)
PROCEDURE RAISE_APPLICATION_ERROR( error_number_in IN NUMBER, error_msg_in IN VARCHAR2);
error_number_in 之容许从 -20000 到 -20999 之间,这样就不会与 ORACLE 的任何错误代码发生冲突。error_msg_in 的长度不能超过 2k,否则截取 2k。
触发器的参数都要在前面加上new.,比如new.type=“Double”,这个很像高级语言中的this
由于BEGIN语句中包含";",会导致mysql直接终结,所有先用DELIMITER修改分隔符为"//"
DELIMITER //
CREATE TRIGGER DoubleRoomPrice
BEFORE INSERT ON Room
FOR EACH ROW
WHEN (new.type="Double" AND new.price<=100)
BEGIN
RAISE_APPLICATION_ERROR(-20000,"所有双人间的价格要大于100镑");
END //
DELIMITER ;
(b)
由于要做比较,此时我们需要声明一个变量vMaxSingleRoomPrice
DELIMITER //
CREATE TRIGGER DoublebiggerSingle
BEFORE INSERT ON Room
FOR EACH Room
WHEN (new.type="Double")
DECLARE vMaxSingleRoomPrice NUMBER;
BEGIN
SELECT MAX(price) INTO vMaxSingleRoomPrice
FROM Room WHERE type="Single";
IF(new.price<vMaxSingleRoomPrice)
RAISE_APPLICATION_ERROR(-20000,"所有双人间的价格要大于最贵的单人间,
其价格是" || vMaxSingleRoomPrice);
END IF;
END //
DELIMITER ;
(c)
不能预订同一房间两次,之前我们在CONSTRAINT约束中用NOT EXISTS做了,用触发器貌似不太好用。
这里我们定义BEFORE触发器,所以功能可以是:
当插入一条数据之前,我们将这条数据的酒店号房间号和订房区域放到表中搜索,如果搜到了重叠的订房区域,就不能再插入
DELIMITER //
CREATE TRIGGER CannotBookRoomTwice
BEFORE INSERT ON Booking
FOR EACH ROW
DECLARE vTwice NUMBER;
BEGIN
SELCET COUNT(*) INTO vTwice FROM Booking
WHERE hotelNo=new.hotelNo AND dateTo>new.dateFrom AND dateFrom<new.dateTo;
IF(vTwice>0)
RAISE_APPLICATION_ERROR(-20000,","不能重复订房间")l;
END IF;
END;
DELIMITER ;
(d)
CREATE TRIGGER BookingGuest
BEFORE INSERT ON Booking
FOR EACH ROW
DECLARE vBookingCount NUMBER;
BEGIN
SELECT COUNT(*) INTO vBookingCount
FROM Booking
WHERE guestNo = new.guestNo
AND dateFrom <= new.dateFrom
AND dateTo >= new.dateTo;
IF (vBookingCount > 0)
raise_application_error(-20000, ‘Guest ‘ || new.guestNo || ‘ is already booked during these dates’);
END IF;
END;
(e)
CREATE TRIGGER BookingAfterInsert
AFTER INSERT ON Booking
FOR EACH ROW
DECLARE
vGuestName Guest.guestName%TYPE;
vGuestAddress Guest.guestAddress%TYPE;
BEGIN
SELECT g.guestName INTO vGuestName, g.guestAddress into vGuestAddress
FROM Guest g, Hotel h
WHERE g.guestNo = new.bookingNo
AND h.hotelNo = new.hotelNo
AND h.city = ‘London’;
IF (vGuestName IS NOT NULL AND vGuestAddress IS NOT NULL)
INSERT INTO GuestAudit
VALUES (vGuestName, vGuestAddress);
END iF;
END;
8.12
解释:INSTEAD OF触发器指定执行触发器而不是执行触发 的SQL 语句,从而替代触发语句的操作。
在表或视图上,每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而,可以在每个具有 INSTEAD OF 触发器的视图上定义视图。
CREATE TRIGGER UpdateLondonHotelRoom
INSTEAD OF INSERT ON LondonHotelRoom
FOR EACH ROW
BEGIN
INSERT INTO Hotel (hotelNo, hotelName, city)
VALUES (new.hotelNo, new.hotelName, "London");
INSERT INTO Room (roomNo, hotelNo, type, price)
VALUES (new.roomNo, new.hotelNo, new.type, new.price);
END;