餐饮点餐系统是一款为餐厅和顾客提供便捷点餐服务的在线平台。
1.DDL
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码',
email VARCHAR(100) UNIQUE COMMENT '邮箱地址',
gender ENUM('男', '女') NOT NULL COMMENT '性别',
phone VARCHAR(20) COMMENT '电话号码'
) COMMENT='用户表';
CREATE TABLE restaurants (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '餐厅ID',
name VARCHAR(100) NOT NULL COMMENT '餐厅名称',
address VARCHAR(255) NOT NULL COMMENT '餐厅地址',
opening_hours VARCHAR(50) COMMENT '营业时间',
contact_number VARCHAR(20) COMMENT '联系电话'
) COMMENT='餐厅表';
CREATE TABLE dish_categories (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '菜品分类ID',
name VARCHAR(50) NOT NULL COMMENT '分类名称',
restaurant_id INT NOT NULL COMMENT '所属餐厅ID',
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE
) COMMENT='菜品分类表';
CREATE TABLE dishes (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '菜品ID',
name VARCHAR(100) NOT NULL COMMENT '菜品名称',
description TEXT COMMENT '菜品描述',
price DECIMAL(10, 2) NOT NULL COMMENT '菜品价格',
category_id INT NOT NULL COMMENT '所属分类ID',
restaurant_id INT NOT NULL COMMENT '所属餐厅ID',
FOREIGN KEY (category_id) REFERENCES dish_categories(id) ON DELETE CASCADE,
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE
) COMMENT='菜品表';
CREATE TABLE carts (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '购物车ID',
user_id INT NOT NULL COMMENT '用户ID',
restaurant_id INT NOT NULL COMMENT '餐厅ID',
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE
) COMMENT='购物车表';
CREATE TABLE cart_items (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '购物车项ID',
cart_id INT NOT NULL COMMENT '购物车ID',
dish_id INT NOT NULL COMMENT '菜品ID',
quantity INT NOT NULL COMMENT '数量',
FOREIGN KEY (cart_id) REFERENCES carts(id) ON DELETE CASCADE,
FOREIGN KEY (dish_id) REFERENCES dishes(id) ON DELETE CASCADE
) COMMENT='购物车项表';
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
user_id INT NOT NULL COMMENT '用户ID',
restaurant_id INT NOT NULL COMMENT '餐厅ID',
order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单日期',
total_price DECIMAL(10, 2) NOT NULL COMMENT '订单总价',
status ENUM('待支付', '已支付', '已取消', '已完成') NOT NULL DEFAULT '待支付' COMMENT '订单状态',
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE
) COMMENT='订单表';
CREATE TABLE order_items (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单项ID',
order_id INT NOT NULL COMMENT '订单ID',
dish_id INT NOT NULL COMMENT '菜品ID',
quantity INT NOT NULL COMMENT '数量',
price_per_item DECIMAL(10, 2) NOT NULL COMMENT '单价',
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (dish_id) REFERENCES dishes(id) ON DELETE CASCADE
) COMMENT='订单项表';
2.DML
-- 插入用户数据
INSERT INTO users (username, password, email, gender, phone) VALUES
('小鱼儿', 'xiaoyu_pass', 'xiaoyu@example.com', '男', '1234567890'),
('花无缺', 'huawu_pass', 'huawu@example.com', '男', '0987654321'),
('苏樱', 'suying_pass', 'suying@example.com', '女', '5555555555');
-- 插入餐厅数据
INSERT INTO restaurants (name, address, opening_hours, contact_number) VALUES
('江湖酒楼', '京城大街1号', '09:00-22:00', '12345678'),
('美味轩', '长安路88号', '10:00-21:30', '87654321'),
('清风阁', '西湖路123号', '11:00-23:00', '98765432');
-- 插入菜品分类数据
INSERT INTO dish_categories (name, restaurant_id) VALUES
('川菜', 1),
('粤菜', 2),
('湘菜', 3),
('鲁菜', 1),
('苏菜', 2),
('浙菜', 3);
-- 插入菜品数据
INSERT INTO dishes (name, description, price, category_id, restaurant_id) VALUES
('水煮鱼', '麻辣鲜香,回味无穷', 58.00, 1, 1),
('宫保鸡丁', '色泽红亮,口感鲜美', 48.00, 1, 1),
('麻婆豆腐', '麻辣可口,下饭佳品', 38.00, 1, 1),
('白切鸡', '皮爽肉滑,鲜美无比', 68.00, 2, 2),
('清蒸鲈鱼', '鲜嫩可口,营养丰富', 78.00, 2, 2),
('菠萝咕噜肉', '酸甜可口,色泽诱人', 52.00, 2, 2),
('剁椒鱼头', '香辣可口,回味无穷', 62.00, 3, 3),
('辣椒炒肉', '香辣可口,下饭佳品', 42.00, 3, 3),
('红烧肉', '肥而不腻,入口即化', 55.00, 3, 3);
- 插入购物车数据(假设小鱼儿在江湖酒楼和美味轩有购物车)
INSERT INTO carts (user_id, restaurant_id) VALUES
(1, 1), -- 小鱼儿在江湖酒楼
(1, 2); -- 小鱼儿在美味轩
-- 获取购物车id
SET @cartId1 = (SELECT id FROM carts WHERE user_id = 1 AND restaurant_id = 1);
SET @cartId2 = (SELECT id FROM carts WHERE user_id = 1 AND restaurant_id = 2);
-- 插入购物车项数据(假设小鱼儿在江湖酒楼选择了前两个菜品,在美味轩选择了后两个菜品)
INSERT INTO cart_items (cart_id, dish_id, quantity) VALUES
(@cartId1, 1, 2), -- 水煮鱼
(@cartId1, 2, 1), -- 宫保鸡丁
(@cartId2, 4, 1), -- 白切鸡
(@cartId2, 5, 3); -- 清蒸鲈鱼
-- 插入订单数据(假设小鱼儿在江湖酒楼下了一个订单)
INSERT INTO orders (user_id, restaurant_id) VALUES
(1, 1); -- 小鱼儿在江湖酒楼下订单
-- 获取订单id
SET @orderId = LAST_INSERT_ID();
-- 插入订单项数据(假设小鱼儿购买了购物车中的水煮鱼和宫保鸡丁)
INSERT INTO order_items (order_id, dish_id, quantity, price_per_item) VALUES
(@orderId, 1, 2, 58.00), -- 菜品:水煮鱼
(@orderId, 2, 1, 48.00); -- 菜品:宫保鸡丁
3.er图以及模型图
4.触发器和测试语句
1.DELIMITER //
CREATE TRIGGER trg_calculate_order_total_before_insert
BEFORE INSERT ON order_items
FOR EACH ROW
BEGIN
-- 计算订单项总价,并更新到订单表中
DECLARE total_price DECIMAL(10, 2) DEFAULT 0.00;
-- 假设这是单个订单项插入,所以直接计算这个订单项的总价
SET total_price = NEW.quantity * NEW.price_per_item;
-- 更新订单的总价(这里假设一个订单只有一个订单项插入,实际中可能需要累加)
UPDATE orders SET total_price = total_price
WHERE id = (SELECT order_id FROM (SELECT order_id FROM order_items WHERE id = NEW.id) AS subquery);
END;
//
DELIMITER ;
-- 注意:这个触发器假设每次只插入一个订单项,并且订单表中total_price字段在插入订单项之前为空或需要更新。
-- 在实际情况中,您可能需要修改这个触发器以处理一个订单中多个订单项的插入,并正确累加总价。
-- 测试语句:插入一个新的订单项,并检查订单表中的总价是否已自动更新 INSERT INTO order_items (order_id, dish_id, quantity, price_per_item) VALUES (@orderId, 3, 1, 38.00); -- 假设我们为同一个订单添加了另一个菜品 SELECT * FROM orders WHERE id = @orderId; -- 检查总价是否已更新
2.DELIMITER //
CREATE TRIGGER trg_log_price_change_after_update
AFTER UPDATE ON dishes
FOR EACH ROW
BEGIN
-- 假设我们有一个价格变动日志表
CREATE TEMPORARY TABLE IF NOT EXISTS price_change_log (
id INT AUTO_INCREMENT PRIMARY KEY,
dish_id INT NOT NULL,
old_price DECIMAL(10, 2) NOT NULL,
new_price DECIMAL(10, 2) NOT NULL,
change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入价格变动记录
INSERT INTO price_change_log (dish_id, old_price, new_price)
VALUES (OLD.id, OLD.price, NEW.price);
END;
//
DELIMITER ;
-- 测试语句:更新一个菜品的价格,然后查询价格变动日志 UPDATE dishes SET price = 60.00 WHERE id = 1; SELECT * FROM price_change_log; -- 你会看到价格变动的记录
3.DELIMITER //
CREATE TRIGGER trg_check_dish_price_before_insert
BEFORE INSERT ON dishes
FOR EACH ROW
BEGIN
-- 假设每个餐厅都有一个最大菜品价格字段,这里为了简化,我们直接在触发器中设定一个值
DECLARE max_price DECIMAL(10, 2) DEFAULT 100.00; -- 示例值,实际情况应从餐厅表中获取
-- 检查插入的菜品价格是否超过餐厅设定的最大价格
IF NEW.price > max_price THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Dish price exceeds the maximum allowed price for the restaurant.';
END IF;
END;
//
DELIMITER ;
-- 测试语句:尝试插入一个价格超过100的菜品,应该会触发错误 INSERT INTO dishes (name, description, price, category_id, restaurant_id) VALUES ('昂贵菜品', '非常昂贵', 150.00, 1, 1); -- 这应该会导致错误
5.存储过程
DELIMITER //
CREATE PROCEDURE GetCartDetailsForUserAndRestaurant(IN userId INT, IN restaurantId INT, OUT cartId INT, OUT totalItems INT)
BEGIN
-- 初始化输出变量
SET cartId = NULL;
SET totalItems = 0;
-- 查询购物车ID和菜品数量
SELECT c.id, COUNT(ci.id)
INTO cartId, totalItems
FROM carts c
JOIN cart_items ci ON c.id = ci.cart_id
WHERE c.user_id = userId AND c.restaurant_id = restaurantId
GROUP BY c.id;
-- 如果没有找到购物车,cartId 保持为 NULL
END //
DELIMITER ;
测试存储过程
-- 假设我们知道小鱼儿在江湖酒楼有购物车
SET @userId = 1;
SET @restaurantId = 1;
SET @cartId = NULL;
SET @totalItems = 0;
CALL GetCartDetailsForUserAndRestaurant(@userId, @restaurantId, @cartId, @totalItems);
SELECT @cartId AS CartID, @totalItems AS TotalItems;
6.简单查询
1.-- 查询所有用户的用户名和性别
SELECT username, gender FROM users;
-- 注释:
-- 使用SELECT语句从users表中检索username(用户名)和gender(性别)字段的所有数据。
2.-- 查询在江湖酒楼餐厅的所有菜品名称和价格
SELECT dishes.name AS dish_name, dishes.price
FROM dishes
JOIN restaurants ON dishes.restaurant_id = restaurants.id
WHERE restaurants.name = '江湖酒楼';
-- 注释:
-- 使用SELECT语句从dishes和restaurants表中检索数据。
-- 通过JOIN操作将dishes表与restaurants表连接起来,基于餐厅ID进行匹配。
-- 使用WHERE子句来过滤出在江湖酒楼餐厅的菜品。
-- 将dishes表中的name字段重命名为dish_name,以便结果集更清晰。
3.
-- 查询小鱼儿的购物车中所有菜品的名称和数量
SELECT dishes.name AS dish_name, cart_items.quantity
FROM cart_items
JOIN dishes ON cart_items.dish_id = dishes.id
JOIN carts ON cart_items.cart_id = carts.id
WHERE carts.user_id = 1;
-- 注释:
-- 使用SELECT语句从cart_items、dishes和carts表中检索数据。
-- 通过JOIN操作将cart_items表与dishes表和carts表连接起来,基于相应的ID进行匹配。
-- 使用WHERE子句来过滤出用户ID为1(即小鱼儿)的购物车中的菜品。
-- 将dishes表中的name字段重命名为dish_name,以便结果集更清晰。
7.复杂查询
1.-- 查询用户“小鱼儿”在“江湖酒楼”餐厅中购买的菜品名称和总价
SELECT
d.name AS dish_name,
SUM(oi.quantity * oi.price_per_item) AS total_price_for_dish
FROM
dishes d
JOIN
order_items oi ON d.id = oi.dish_id
JOIN
orders o ON oi.order_id = o.id
JOIN
users u ON o.user_id = u.id
WHERE
u.username = '小鱼儿'
AND o.restaurant_id = (SELECT id FROM restaurants WHERE name = '江湖酒楼')
GROUP BY
d.id, d.name;
-- 注释:
-- 这个查询首先连接了dishes、order_items、orders和users表,
-- 通过用户名和餐厅名称筛选出“小鱼儿”在“江湖酒楼”购买的菜品,
-- 然后使用GROUP BY按菜品名称分组, -- 并计算每个菜品的总价(数量乘以单价)。
2.-- 查询每个餐厅的菜品分类数量,并按数量降序排列
SELECT
r.name AS restaurant_name,
COUNT(DISTINCT dc.id) AS number_of_categories
FROM
restaurants r
JOIN
dish_categories dc ON r.id = dc.restaurant_id
GROUP BY
r.id, r.name
ORDER BY
number_of_categories DESC;
-- 注释:
-- 这个查询连接了restaurants和dish_categories表,
-- 通过餐厅ID关联它们,
-- 然后使用COUNT(DISTINCT ...)计算每个餐厅的不同菜品分类数量,
-- 最后使用ORDER BY按数量降序排列结果。
3.-- 查询每个用户的订单数量和总金额,只显示有订单的用户
SELECT
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_price) AS total_spent
FROM
users u
LEFT JOIN
orders o ON u.id = o.user_id
GROUP BY
u.id, u.username
HAVING
COUNT(o.id) > 0;
-- 注释:
-- 这个查询首先通过LEFT JOIN连接了users和orders表,
-- 然后使用GROUP BY按用户ID和用户名分组,
-- 使用COUNT计算每个用户的订单数量,
-- 使用SUM计算每个用户的订单总金额,
-- 最后使用HAVING子句筛选出有订单的用户(即订单数量大于0的用户)。
总结:通过该系统,餐厅能够展示其菜单,顾客可以浏览菜品,并将其加入购物车或直接下单。系统还提供了订单管理功能,方便餐厅跟踪和处理顾客的订单。