MySQL多表查询、事务与索引的实践与应用
摘要:本文围绕MySQL数据库操作展开,通过构建部门与员工管理、餐饮业务相关的数据库表,并填充测试数据,系统地阐述了多表查询的多种方式,包括内连接、外连接和不同类型的子查询,同时介绍了事务的处理以及索引的创建、查询和删除操作。
关键词:MySQL;多表查询;事务;索引
一、引言
在数据库管理与开发过程中,多表查询、事务管理以及索引优化是提升数据处理效率和数据完整性的关键技术。本文通过实际案例详细展示这些技术在MySQL数据库中的具体应用。
二、数据准备
2.1 部门与员工表的创建及数据插入
- 部门表(
tb_dept
):用于存储部门相关信息。
CREATE TABLE tb_dept(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(10) NOT NULL UNIQUE COMMENT '部门名称',
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '修改时间'
) COMMENT '部门表';
INSERT INTO tb_dept (id, name, create_time, update_time) VALUES
(1, '学工部', NOW(), NOW()),
(2, '教研部', NOW(), NOW()),
(3, '咨询部', NOW(), NOW()),
(4, '就业部', NOW(), NOW()),
(5, '人事部', NOW(), NOW());
- 员工表(
tb_emp
):通过dept_id
与部门表关联,记录员工详细信息。
CREATE TABLE tb_emp (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(32) DEFAULT '123456' COMMENT '密码',
name VARCHAR(10) NOT NULL COMMENT '姓名',
gender TINYINT UNSIGNED NOT NULL COMMENT '性别, 说明: 1 男, 2 女',
image VARCHAR(300) COMMENT '图像',
job TINYINT UNSIGNED COMMENT '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
entrydate DATE COMMENT '入职时间',
dept_id INT UNSIGNED COMMENT '部门ID',
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '修改时间'
) COMMENT '员工表';
INSERT INTO tb_emp(id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time) VALUES
(1, 'jinyong', '123456', '金庸', 1, '1.jpg', 4, '2000 - 01 - 01', 2, NOW(), NOW()),
(2, 'zhangwuji', '123456', '张无忌', 1, '2.jpg', 2, '2015 - 01 - 01', 2, NOW(), NOW()),
-- 省略部分插入数据
(17, 'chenyouliang', '123456', '陈友谅', 1, '17.jpg', NULL, '2015 - 03 - 21', NULL, NOW(), NOW());
2.2 餐饮业务相关表的创建及数据插入
- 分类表(
category
):区分菜品分类与套餐分类。
CREATE TABLE category(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(20) NOT NULL UNIQUE COMMENT '分类名称',
type TINYINT UNSIGNED NOT NULL COMMENT '类型 1 菜品分类 2 套餐分类',
sort TINYINT UNSIGNED NOT NULL COMMENT '顺序',
status TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态 0 禁用,1 启用',
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '更新时间'
) COMMENT '分类';
- 菜品表(
dish
):记录菜品的各项属性,与分类表通过category_id
关联。
CREATE TABLE dish(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(20) NOT NULL UNIQUE COMMENT '菜品名称',
category_id INT UNSIGNED NOT NULL COMMENT '菜品分类ID',
price DECIMAL(8, 2) NOT NULL COMMENT '菜品价格',
image VARCHAR(300) NOT NULL COMMENT '菜品图片',
description VARCHAR(200) COMMENT '描述信息',
status TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态, 0 停售 1 起售',
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '更新时间'
) COMMENT '菜品';
- 套餐表(
setmeal
):存储套餐信息,与分类表通过category_id
关联。
CREATE TABLE setmeal(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(20) NOT NULL UNIQUE COMMENT '套餐名称',
category_id INT UNSIGNED NOT NULL COMMENT '分类id',
price DECIMAL(8, 2) NOT NULL COMMENT '套餐价格',
image VARCHAR(300) NOT NULL COMMENT '图片',
description VARCHAR(200) COMMENT '描述信息',
status TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态 0:停用 1:启用',
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '更新时间'
) COMMENT '套餐';
- 套餐菜品关联表(
setmeal_dish
):建立套餐与菜品之间的联系。
CREATE TABLE setmeal_dish(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
setmeal_id INT UNSIGNED NOT NULL COMMENT '套餐id ',
dish_id INT UNSIGNED NOT NULL COMMENT '菜品id',
copies TINYINT UNSIGNED NOT NULL COMMENT '份数'
) COMMENT '套餐菜品中间表';
- 插入测试数据:向上述餐饮业务相关表插入大量测试数据,涵盖各类菜品、套餐及其关联信息。
三、多表查询操作
3.1 基本多表查询
通过连接 tb_emp
和 tb_dept
表,获取员工所属部门信息。
SELECT * FROM tb_emp, tb_dept WHERE tb_emp.dept_id = tb_dept.id;
3.2 内连接
- 隐式内连接:查询员工姓名及所属部门名称,可通过起别名提高可读性。
SELECT tb_emp.name, tb_dept.name FROM tb_emp, tb_dept WHERE tb_emp.dept_id = tb_dept.id;
SELECT e.name, d.name FROM tb_emp e, tb_dept d WHERE e.dept_id = d.id;
- 显式内连接:同样实现查询员工姓名及所属部门名称。
SELECT tb_emp.name, tb_dept.name FROM tb_emp JOIN tb_dept ON tb_emp.dept_id = tb_dept.id;
3.3 外连接
- 左外连接:获取员工表所有员工姓名及对应的部门名称,包括无部门员工。
SELECT e.name, d.name FROM tb_emp e LEFT JOIN tb_dept d ON e.dept_id = d.id;
- 右外连接:获取部门表所有部门名称及对应的员工名称,包括无员工部门。
SELECT e.name, d.name FROM tb_emp e RIGHT JOIN tb_dept d ON e.dept_id = d.id;
-- 等同于
SELECT e.name, d.name FROM tb_dept d LEFT JOIN tb_emp e ON e.dept_id = d.id;
3.4 子查询
- 标量子查询:
- 查询“教研部”的所有员工信息,先获取教研部
id
,再查询该部门员工。
- 查询“教研部”的所有员工信息,先获取教研部
SELECT id FROM tb_dept WHERE name = '教研部';
SELECT * FROM tb_emp WHERE dept_id = (SELECT id FROM tb_dept WHERE name = '教研部');
- 查询在“方东白”入职之后的员工信息,先获取方东白入职时间,再查询晚于该时间入职的员工。
SELECT entrydate FROM tb_emp WHERE name = '方东白';
SELECT * FROM tb_emp WHERE entrydate > (SELECT entrydate FROM tb_emp WHERE name = '方东白');
- 列子查询:查询“教研部”和“咨询部”的所有员工信息,先获取两个部门
id
,再查询对应部门员工。
SELECT id FROM tb_dept WHERE name = '教研部' OR name = '咨询部';
SELECT * FROM tb_emp WHERE dept_id IN (SELECT id FROM tb_dept WHERE name = '教研部' OR name = '咨询部');
- 行子查询:查询与“韦一笑”入职日期及职位都相同的员工信息,可通过两种方式实现。
SELECT entrydate, job FROM tb_emp WHERE name = '韦一笑';
-- 方式一
SELECT * FROM tb_emp WHERE entrydate = (SELECT entrydate FROM tb_emp WHERE name = '韦一笑') AND job = (SELECT job FROM tb_emp WHERE name = '韦一笑');
-- 方式二
SELECT * FROM tb_emp WHERE (entrydate, job) = (SELECT entrydate, job FROM tb_emp WHERE name = '韦一笑');
- 表子查询:查询入职日期在“2006 - 01 - 01”之后的员工信息及其部门名称,先获取符合日期条件的员工,再连接部门表获取部门名称。
SELECT * FROM tb_emp WHERE entrydate > '2006 - 01 - 01';
SELECT e.*, d.name FROM (SELECT * FROM tb_emp WHERE entrydate > '2006 - 01 - 01') e, tb_dept d WHERE e.dept_id = d.id;
3.5 餐饮业务多表查询需求
- 查询低价菜品信息:获取价格低于10元的菜品名称、价格及分类名称。
SELECT d.name, d.price, c.name
FROM dish d, category c
WHERE d.category_id = c.id AND d.price < 10;
- 查询特定价格与状态菜品信息:查询价格在10元(含)到50元(含)之间且状态为“起售”的菜品信息,包括无分类菜品。
SELECT d.name, d.price, c.name
FROM dish d LEFT JOIN category c ON d.category_id = c.id
WHERE d.price BETWEEN 10 AND 50 AND d.status = 1;
- 查询各分类最贵菜品信息:展示每个分类下最贵菜品的分类名称和价格。
SELECT c.name, MAX(d.price)
FROM dish d, category c
WHERE d.category_id = c.id
GROUP BY c.name;
- 查询特定条件分类名称:获取菜品状态为“起售”且菜品数量大于等于3的分类名称。
SELECT c.name, COUNT(*)
FROM dish d, category c
WHERE d.category_id = c.id AND d.status = 1
GROUP BY c.name
HAVING COUNT(*) >= 3;
- 查询套餐包含菜品信息:展示“商务套餐A”包含的菜品相关信息。
SELECT s.name, s.price, d.name, d.price, sd.copies
FROM setmeal s, setmeal_dish sd, dish d
WHERE s.id = sd.setmeal_id AND sd.dish_id = d.id AND s.name = '商务套餐A';
- 查询低于平均价格菜品信息:先计算菜品平均价格,再查询低于该平均价格的菜品。
SELECT AVG(price) FROM dish;
SELECT * FROM dish WHERE price < (SELECT AVG(price) FROM dish);
四、事务操作
4.1 事务处理流程
在删除部门及相关员工操作中,使用事务确保数据一致性。
-- 开启事务
START TRANSACTION;
-- 删除部门
DELETE FROM tb_dept WHERE id = 2;
-- 删除部门下的员工
DELETE FROM tb_emp WHERE dept_id = 2;
-- 提交事务
COMMIT;
-- 回滚事务(若中途出错)
ROLLBACK;
SELECT * FROM tb_dept;
SELECT * FROM tb_emp;
五、索引操作
5.1 索引的创建、查询与删除
- 创建索引:为
tb_sku
表的sn
字段和tb_emp
表的name
字段创建索引。
CREATE INDEX idx_sku_sn ON tb_sku(sn);
CREATE INDEX idx_emp_name ON tb_emp(name);
- 查询索引信息:查看
tb_emp
表的索引情况。
SHOW INDEX FROM tb_emp;
- 删除索引:删除
tb_emp
表中name
字段的索引。
DROP INDEX idx_emp_name ON tb_emp;