Almalinux_10.0下MySQL的多表操作与函数使用
一、AlmaLinux 10.0 环境搭建 MySQL
1.1 安装 MySQL 服务
AlmaLinux 10.0 作为 Red Hat Enterprise Linux 的分支,采用 DNF 包管理器进行软件安装,以下是在 AlmaLinux 10.0 上安装 MySQL 8.0 的完整步骤:
bash
# 更新系统软件包
sudo dnf update -y# 安装 MySQL 服务器
sudo dnf install mysql-server -y# 启动 MySQL 服务
sudo systemctl start mysqld# 设置 MySQL 服务开机自启动
sudo systemctl enable mysqld# 查看 MySQL 服务状态
sudo systemctl status mysqld
1.2 初始化 MySQL 安全配置
MySQL 安装后需要进行初始化安全配置,包括设置 root 密码和移除测试账户:
bash
# 运行安全配置脚本
sudo mysql_secure_installation
执行上述命令后,会提示进行以下配置:
- 输入当前 root 密码(首次运行时为空,直接回车)
- 设置新的 root 密码
- 是否移除匿名用户账户:
y
- 是否禁止 root 远程登录:
y
- 是否移除测试数据库:
y
- 是否重新加载权限表:
y
1.3 登录 MySQL 并创建测试环境
完成安装和配置后,使用以下命令登录 MySQL:
bash
# 以 root 用户登录 MySQL
mysql -u root -p
登录后创建测试数据库和表,用于后续的多表操作和函数演示:
sql
-- 创建测试数据库
CREATE DATABASE mydb3;
CREATE DATABASE mydb4;-- 使用 mydb3 数据库
USE mydb3;-- 创建部门表 dept3
CREATE TABLE dept3 (deptno INT PRIMARY KEY,name VARCHAR(50),location VARCHAR(50)
);-- 创建员工表 emp3
CREATE TABLE emp3 (eid INT PRIMARY KEY,ename VARCHAR(50),age INT,dept_id INT,FOREIGN KEY (dept_id) REFERENCES dept3 (deptno)
);-- 插入部门数据
INSERT INTO dept3 VALUES (1001, '研发部', '北京');
INSERT INTO dept3 VALUES (1002, '销售部', '上海');
INSERT INTO dept3 VALUES (1003, '财务部', '广州');
INSERT INTO dept3 VALUES (1004, '人事部', '深圳');-- 插入员工数据
INSERT INTO emp3 VALUES (1, '张三', 25, 1001);
INSERT INTO emp3 VALUES (2, '李四', 30, 1001);
INSERT INTO emp3 VALUES (3, '王五', 22, 1002);
INSERT INTO emp3 VALUES (4, '赵六', 28, 1002);
INSERT INTO emp3 VALUES (5, '钱七', 35, 1003);
INSERT INTO emp3 VALUES (6, '孙八', 29, NULL); -- 无部门员工-- 使用 mydb4 数据库
USE mydb4;-- 创建员工表 employee
CREATE TABLE employee (dname VARCHAR(20),eid VARCHAR(20),ename VARCHAR(20),hiredate DATE,salary DOUBLE
);-- 插入员工数据
INSERT INTO employee VALUES('研发部','1001','刘备','2021-11-01',3000);
INSERT INTO employee VALUES('研发部','1002','关羽','2021-11-02',5000);
INSERT INTO employee VALUES('研发部','1003','张飞','2021-11-03',7000);
INSERT INTO employee VALUES('研发部','1004','赵云','2021-11-04',7000);
INSERT INTO employee VALUES('研发部','1005','马超','2021-11-05',4000);
INSERT INTO employee VALUES('研发部','1006','黄忠','2021-11-06',4000);
INSERT INTO employee VALUES('销售部','1007','曹操','2021-11-01',2000);
INSERT INTO employee VALUES('销售部','1008','许褚','2021-11-02',3000);
INSERT INTO employee VALUES('销售部','1009','典韦','2021-11-03',5000);
INSERT INTO employee VALUES('销售部','1010','张辽','2021-11-04',6000);
INSERT INTO employee VALUES('销售部','1011','徐晃','2021-11-05',9000);
INSERT INTO employee VALUES('销售部','1012','曹洪','2021-11-06',6000);
二、MySQL 多表操作实战
2.1 外连接查询
外连接查询用于获取两个表中满足连接条件的数据,同时保留一侧或两侧表中不满足条件的数据,分为左外连接、右外连接和满外连接。
2.1.1 左外连接(LEFT OUTER JOIN)
左外连接保留左表的所有记录,右表只保留满足连接条件的记录:
sql
-- 查询哪些部门有员工,哪些部门没有员工
USE mydb3;
SELECT * FROM dept3 LEFT OUTER JOIN emp3 ON dept3.deptno = emp3.dept_id;
上述查询结果中,即使某个部门没有员工,也会显示该部门的信息,对应的员工信息为 NULL。
2.1.2 右外连接(RIGHT OUTER JOIN)
右外连接保留右表的所有记录,左表只保留满足连接条件的记录:
sql
-- 查询哪些员工有对应的部门,哪些没有
SELECT * FROM dept3 RIGHT OUTER JOIN emp3 ON dept3.deptno = emp3.dept_id;
此查询会显示所有员工记录,包括没有部门的员工(如 ID 为 6 的员工),对应的部门信息为 NULL。
2.1.3 满外连接(FULL OUTER JOIN)
MySQL 对满外连接支持不佳,可通过 UNION 合并左外连接和右外连接的结果实现:
sql
-- 使用 UNION 实现满外连接
SELECT * FROM dept3 LEFT OUTER JOIN emp3 ON dept3.deptno = emp3.dept_id
UNION
SELECT * FROM dept3 RIGHT OUTER JOIN emp3 ON dept3.deptno = emp3.dept_id;
2.2 子查询进阶
子查询是在一个查询语句中嵌套另一个查询,用于解决复杂的查询需求,根据返回结果可分为单行单列、单行多列、多行单列和多行多列四种类型。
2.2.1 单行单列子查询
sql
-- 查询年龄最大的员工信息
SELECT eid, ename, age FROM emp3 WHERE age = (SELECT MAX(age) FROM emp3);
2.2.2 多行单列子查询
sql
-- 查询研发部和销售部的员工信息
SELECT eid, ename FROM emp3 WHERE dept_id IN (SELECT deptno FROM dept3 WHERE name = '研发部' OR name = '销售部');
2.2.3 子查询关键字
ALL 关键字
ALL 用于与子查询返回的所有值比较:
sql
-- 查询年龄大于'1003'部门所有员工年龄的员工
SELECT * FROM emp3 WHERE age > ALL(SELECT age FROM emp3 WHERE dept_id = '1003');-- 查询不属于任何一个部门的员工
SELECT * FROM emp3 WHERE dept_id != ALL(SELECT deptno FROM dept3);
ANY/SOME 关键字
ANY 用于与子查询返回的任意一个值比较,SOME 是 ANY 的别名:
sql
-- 查询年龄大于'1003'部门任意一个员工年龄的员工
SELECT * FROM emp3 WHERE age > ANY(SELECT age FROM emp3 WHERE dept_id = '1003');
IN 关键字
IN 用于判断值是否在子查询结果集中:
sql
-- 查询研发部和销售部的员工信息(IN 关键字版本)
SELECT eid, ename, t.name FROM emp3 WHERE dept_id IN (SELECT deptno, name FROM dept3 WHERE name = '研发部' OR name = '销售部');
EXISTS 关键字
EXISTS 用于判断子查询是否有结果返回,效率高于 IN:
sql
-- 查询公司是否有大于60岁的员工
SELECT * FROM emp3 a WHERE EXISTS(SELECT * FROM emp3 b WHERE a.age > 60);-- 查询有所属部门的员工信息
SELECT * FROM emp3 a WHERE EXISTS(SELECT * FROM dept3 b WHERE a.dept_id = b.deptno);
2.3 自关联查询
自关联查询是将一张表当作多张表使用,用于处理层级关系数据:
sql
-- 创建自关联表 t_sanguo
CREATE TABLE t_sanguo (eid INT PRIMARY KEY,ename VARCHAR(20),manager_id INT,FOREIGN KEY (manager_id) REFERENCES t_sanguo (eid)
);-- 插入数据
INSERT INTO t_sanguo VALUES(1, '刘协', NULL);
INSERT INTO t_sanguo VALUES(2, '刘备', 1);
INSERT INTO t_sanguo VALUES(3, '关羽', 2);
INSERT INTO t_sanguo VALUES(4, '张飞', 2);
INSERT INTO t_sanguo VALUES(5, '曹操', 1);
INSERT INTO t_sanguo VALUES(6, '许褚', 5);
INSERT INTO t_sanguo VALUES(7, '典韦', 5);
INSERT INTO t_sanguo VALUES(8, '孙权', 1);
INSERT INTO t_sanguo VALUES(9, '周瑜', 8);
INSERT INTO t_sanguo VALUES(10, '鲁肃', 8);-- 查询每个三国人物及他的上级信息
SELECT * FROM t_sanguo a, t_sanguo b WHERE a.manager_id = b.eid;
三、MySQL 函数深度解析
3.1 聚合函数
聚合函数用于对一组数据进行计算,返回单个值,除了常见的 COUNT、SUM、MIN、MAX、AVG 外,重点介绍 group_concat () 函数。
3.1.1 group_concat () 函数
group_concat () 用于将分组中的值连接成一个字符串:
sql
-- 将所有员工的名字合并成一行
SELECT group_concat(emp_name) FROM emp;-- 按部门分组,用分号分隔员工名字
SELECT department, group_concat(emp_name SEPARATOR ';') FROM emp GROUP BY department;-- 按薪资降序排序后合并员工名字
SELECT department, group_concat(emp_name ORDER BY salary DESC SEPARATOR ';') FROM emp GROUP BY department;
3.2 数学函数
数学函数用于数值计算,以下是常用数学函数示例:
sql
-- 绝对值
SELECT ABS(-1); -- 1-- 向上取整
SELECT CEIL(1.5); -- 2-- 向下取整
SELECT FLOOR(1.5); -- 1-- 最大值
SELECT GREATEST(3, 12, 34, 8, 25); -- 34-- 最小值
SELECT LEAST(3, 12, 34, 8, 25); -- 3-- 取模
SELECT MOD(5, 2); -- 1-- 圆周率
SELECT PI(); -- 3.141593-- 幂运算
SELECT POW(2, 3); -- 8-- 随机数
SELECT RAND(); -- 0.93099315644334(示例值)-- 四舍五入
SELECT ROUND(1.23456); -- 1
SELECT ROUND(1.23456, 3); -- 1.235-- 截断
SELECT TRUNCATE(1.23456, 3); -- 1.234
3.3 字符串函数
字符串函数用于处理字符串数据,以下是常用字符串函数示例:
sql
-- 字符数
SELECT CHAR_LENGTH("RUNOOB"); -- 6-- 字符串合并
SELECT CONCAT("SQL ", "Runoob ", "Gooogle ", "Facebook"); -- SQL Runoob Gooogle Facebook-- 带分隔符合并
SELECT CONCAT_WS("-", "SQL", "Tutorial", "is", "fun!"); -- SQL-Tutorial-is-fun!-- 去除左空格
SELECT LTRIM(" RUNOOB"); -- RUNOOB-- 去除右空格
SELECT RTRIM("RUNOOB "); -- RUNOOB-- 去除首尾空格
SELECT TRIM(" RUNOOB "); -- RUNOOB-- 子字符串截取
SELECT MID("RUNOOB", 2, 3); -- UNO
SELECT SUBSTR("RUNOOB", 2, 3); -- UNO-- 字符串替换
SELECT REPLACE('abc', 'a', 'x'); -- xbc-- 字符串反转
SELECT REVERSE('abc'); -- cba-- 取右子串
SELECT RIGHT('runoob', 2); -- ob-- 大小写转换
SELECT UCASE("runoob"); -- RUNOOB
SELECT LCASE("RUNOOB"); -- runoob
3.4 日期函数
日期函数用于处理日期和时间数据,以下是常用日期函数示例:
sql
-- 获取当前时间戳
SELECT UNIX_TIMESTAMP(); -- 1632729059(示例值)-- 时间戳转日期
SELECT FROM_UNIXTIME(1598079966, '%Y-%m-%d %H:%i:%s'); -- 2020-08-22 15:06:06-- 获取当前日期
SELECT CURDATE(); -- 2023-10-01(示例值)-- 获取当前时间
SELECT CURTIME(); -- 14:30:22(示例值)-- 获取当前日期时间
SELECT CURRENT_TIMESTAMP(); -- 2023-10-01 14:30:22(示例值)-- 日期差值
SELECT DATEDIFF('2023-01-01', '2023-02-02'); -- -32-- 时间差值
SELECT TIMEDIFF("13:10:11", "13:10:10"); -- 00:00:01-- 日期格式化
SELECT DATE_FORMAT('2023-10-01 14:30:22', '%Y-%m-%d %r'); -- 2023-10-01 02:30:22 PM-- 字符串转日期
SELECT STR_TO_DATE("August 10 2023", "%M %d %Y"); -- 2023-08-10-- 日期加减
SELECT DATE_ADD("2023-06-15", INTERVAL 10 DAY); -- 2023-06-25
SELECT DATE_SUB("2023-06-15", INTERVAL 3 HOUR); -- 2023-06-15 11:30:22(假设原时间为14:30:22)-- 提取日期部分
SELECT EXTRACT(MONTH FROM '2023-10-01 14:30:22'); -- 10-- 获取月份最后一天
SELECT LAST_DAY("2023-06-20"); -- 2023-06-30-- 获取年份、月份、日期
SELECT YEAR("2023-06-15"); -- 2023
SELECT MONTH("2023-06-15"); -- 6
SELECT DAY("2023-06-15"); -- 15
3.5 控制流函数
控制流函数用于条件判断,以下是常用控制流函数示例:
sql
-- IF 函数
SELECT IF(1 > 0, '正确', '错误'); -- 正确-- IFNULL 函数
SELECT IFNULL(NULL, 'Hello Word'); -- Hello Word-- ISNULL 函数
SELECT ISNULL(NULL); -- 1-- NULLIF 函数
SELECT NULLIF(25, 25); -- NULL-- CASE WHEN 语句
SELECT CASE 100 WHEN 50 THEN 'tom' WHEN 100 THEN 'mary' ELSE 'tim' END; -- mary-- 订单支付类型转换
SELECT *,CASE WHEN payType=1 THEN '微信支付' WHEN payType=2 THEN '支付宝支付' WHEN payType=3 THEN '银行卡支付' ELSE '其他支付方式' END AS payTypeStr
FROM orders;
3.6 窗口函数(MySQL 8.0 新增)
窗口函数用于在查询结果中进行分组排序和计算,是 MySQL 8.0 的重要特性。
3.6.1 序号函数
序号函数包括 ROW_NUMBER ()、RANK ()、DENSE_RANK (),用于分组排序:
sql
-- 对每个部门的员工按薪资排序(ROW_NUMBER)
SELECT dname,ename,salary,ROW_NUMBER() OVER(PARTITION BY dname ORDER BY salary DESC) AS rn
FROM employee;-- 对每个部门的员工按薪资排序(RANK)
SELECT dname,ename,salary,RANK() OVER(PARTITION BY dname ORDER BY salary DESC) AS rn
FROM employee;-- 对每个部门的员工按薪资排序(DENSE_RANK)
SELECT dname,ename,salary,DENSE_RANK() OVER(PARTITION BY dname ORDER BY salary DESC) AS rn
FROM employee;-- 分组求TOP3员工
SELECT * FROM (SELECT dname,ename,salary,DENSE_RANK() OVER(PARTITION BY dname ORDER BY salary DESC) AS rnFROM employee
) t WHERE t.rn <= 3;
3.6.2 开窗聚合函数
开窗聚合函数在窗口内动态计算聚合值:
sql
-- 按部门和入职日期计算累计薪资
SELECT dname,ename,salary,SUM(salary) OVER(PARTITION BY dname ORDER BY hiredate) AS pv1
FROM employee;-- 计算当前行及前3行的薪资和
SELECT dname,ename,salary,SUM(salary) OVER(PARTITION BY dname ORDER BY hiredate ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS c1
FROM employee;
3.6.3 分布函数
分布函数包括 CUME_DIST 和 PERCENT_RANK,用于计算数据分布:
sql
-- CUME_DIST 计算小于等于当前薪资的比例
SELECT dname,ename,salary,CUME_DIST() OVER(ORDER BY salary) AS rn1,CUME_DIST() OVER(PARTITION BY dname ORDER BY salary) AS rn2
FROM employee;-- PERCENT_RANK 计算相对排名
SELECT dname,ename,salary,RANK() OVER(PARTITION BY dname ORDER BY salary DESC) AS rn,PERCENT_RANK() OVER(PARTITION BY dname ORDER BY salary DESC) AS rn2
FROM employee;
3.6.4 前后函数
前后函数包括 LAG 和 LEAD,用于获取前后行数据:
sql
-- LAG 函数获取前n行数据
SELECT dname,ename,hiredate,salary,LAG(hiredate, 1, '2000-01-01') OVER(PARTITION BY dname ORDER BY hiredate) AS last_1_time,LAG(hiredate, 2) OVER(PARTITION BY dname ORDER BY hiredate) AS last_2_time
FROM employee;-- LEAD 函数获取后n行数据
SELECT dname,ename,hiredate,salary,LEAD(hiredate, 1, '2000-01-01') OVER(PARTITION BY dname ORDER BY hiredate) AS next_1_time,LEAD(hiredate, 2) OVER(PARTITION BY dname ORDER BY hiredate) AS next_2_time
FROM employee;
3.6.5 头尾函数
头尾函数包括 FIRST_VALUE 和 LAST_VALUE,用于获取窗口内的第一个和最后一个值:
sql
-- 获取每个部门第一个和最后一个入职员工的薪资
SELECTdname,ename,hiredate,salary,FIRST_VALUE(salary) OVER(PARTITION BY dname ORDER BY hiredate) AS first_salary,LAST_VALUE(salary) OVER(PARTITION BY dname ORDER BY hiredate) AS last_salary
FROM employee;
3.6.6 其他函数
包括 NTH_VALUE 和 NTILE,用于获取第 n 个值和数据分桶:
sql
-- 获取每个部门第2和第3个入职员工的薪资
SELECT dname,ename,hiredate,salary,NTH_VALUE(salary, 2) OVER(PARTITION BY dname ORDER BY hiredate) AS second_salary,NTH_VALUE(salary, 3) OVER(PARTITION BY dname ORDER BY hiredate) AS third_salary
FROM employee;-- 按入职日期将每个部门的员工分成3组
SELECT dname,ename,hiredate,salary,NTILE(3) OVER(PARTITION BY dname ORDER BY hiredate) AS rn
FROM employee;-- 取出每个部门的第一组员工
SELECT * FROM (SELECT dname,ename,hiredate,salary,NTILE(3) OVER(PARTITION BY dname ORDER BY hiredate) AS rn FROM employee
) t WHERE t.rn = 1;
四、总结与实践建议
通过本文的学习,我们在 AlmaLinux 10.0 环境下系统地掌握了 MySQL 的多表操作和各类函数的使用,主要包括:
-
多表操作:外连接查询(左连接、右连接、满连接)、子查询(单行单列、多行单列、子查询关键字)、自关联查询,这些操作是处理复杂数据关系的基础。
-
函数体系:涵盖聚合函数、数学函数、字符串函数、日期函数、控制流函数和窗口函数,尤其是 MySQL 8.0 新增的窗口函数,极大提升了复杂查询的处理能力。
实践建议:
-
多表操作练习:
- 设计一个小型企业管理系统,包含部门、员工、薪资等表,练习外连接和子查询
- 构建层级关系数据(如组织结构、分类体系),练习自关联查询
-
函数应用实践:
- 使用字符串函数处理员工姓名、地址等文本数据
- 利用日期函数分析员工入职时间、薪资发放周期
- 深入理解窗口函数,尝试解决分组排序、TOPN 查询、数据分桶等实际问题
-
性能优化思考:
- 对比 EXISTS 和 IN 的执行效率,理解其适用场景
- 分析窗口函数与传统分组聚合的性能差异
- 考虑在大数据量下如何优化多表查询和复杂函数的使用
通过持续的实践和总结,您将能够熟练运用 MySQL 的各种功能,解决实际开发中的数据查询和处理需求。AlmaLinux 10.0 提供了稳定的运行环境,配合 MySQL 8.0 的强大功能,足以支撑中小型应用的数据存储和查询需求。