MySQL高级特性详解

MySQL高级特性详解

一、自关联查询

概念

自关联查询是指一个表与它自己进行连接的查询。通常用于处理具有层级关系或递归结构的数据。

应用场景

  • 员工与上级关系
  • 分类的父子关系
  • 地区的层级关系

示例

sql 复制代码
-- 创建员工表
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    manager_id INT,
    FOREIGN KEY (manager_id) REFERENCES employees(emp_id)
);

-- 插入示例数据
INSERT INTO employees VALUES 
(1, '张总', NULL),
(2, '李经理', 1),
(3, '王主管', 2),
(4, '赵员工', 3),
(5, '钱员工', 3);

-- 查询每个员工及其直接上级
SELECT 
    e1.emp_name AS '员工',
    e2.emp_name AS '上级'
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.emp_id;

-- 查询所有下属超过1人的管理者
SELECT 
    e2.emp_name AS '管理者',
    COUNT(e1.emp_id) AS '下属人数'
FROM employees e1
INNER JOIN employees e2 ON e1.manager_id = e2.emp_id
GROUP BY e2.emp_id, e2.emp_name
HAVING COUNT(e1.emp_id) > 1;

二、子查询操作

概念

子查询是嵌套在其他SQL语句中的SELECT语句,可以出现在WHERE、FROM、SELECT等子句中。

子查询分类

1. 标量子查询(返回单个值)
sql 复制代码
-- 查询工资高于平均工资的员工
SELECT emp_name, salary 
FROM employees 
WHERE salary > (SELECT AVG(salary) FROM employees);
2. 列子查询(返回一列多行)
sql 复制代码
-- 使用IN操作符
SELECT * FROM employees 
WHERE dept_id IN (SELECT dept_id FROM departments WHERE location = '北京');

-- 使用ANY/ALL操作符
SELECT * FROM employees 
WHERE salary > ANY (SELECT salary FROM employees WHERE dept_id = 10);
3. 行子查询(返回一行多列)
sql 复制代码
-- 查询与张三相同部门和职位的员工
SELECT * FROM employees 
WHERE (dept_id, position) = (
    SELECT dept_id, position 
    FROM employees 
    WHERE emp_name = '张三'
);
4. 表子查询(返回多行多列)
sql 复制代码
-- FROM子句中的子查询
SELECT t.dept_name, t.avg_salary
FROM (
    SELECT d.dept_name, AVG(e.salary) AS avg_salary
    FROM employees e
    JOIN departments d ON e.dept_id = d.dept_id
    GROUP BY d.dept_name
) t
WHERE t.avg_salary > 10000;

EXISTS子查询

sql 复制代码
-- 查询有员工的部门
SELECT * FROM departments d
WHERE EXISTS (
    SELECT 1 FROM employees e 
    WHERE e.dept_id = d.dept_id
);

-- 查询没有下属的员工
SELECT * FROM employees e1
WHERE NOT EXISTS (
    SELECT 1 FROM employees e2 
    WHERE e2.manager_id = e1.emp_id
);

三、窗口函数

概念

窗口函数在保留原表所有行的基础上,为每一行计算聚合值。与GROUP BY不同,它不会减少返回的行数。

基本语法

sql 复制代码
函数名() OVER (
    [PARTITION BY 列名]
    [ORDER BY 列名]
    [窗口子句]
)

常用窗口函数

1. 聚合窗口函数
sql 复制代码
-- 计算累计销售额
SELECT 
    order_date,
    amount,
    SUM(amount) OVER (ORDER BY order_date) AS cumulative_sum,
    AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM orders;

-- 按部门计算工资占比
SELECT 
    emp_name,
    dept_id,
    salary,
    salary / SUM(salary) OVER (PARTITION BY dept_id) * 100 AS salary_percentage
FROM employees;
2. 排名函数
sql 复制代码
-- ROW_NUMBER(): 生成唯一序号
-- RANK(): 相同值同排名,下一个排名会跳过
-- DENSE_RANK(): 相同值同排名,下一个排名连续

SELECT 
    emp_name,
    dept_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS row_num,
    RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank_num,
    DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dense_rank_num
FROM employees;

-- NTILE(): 将数据分成N组
SELECT 
    emp_name,
    salary,
    NTILE(4) OVER (ORDER BY salary) AS quartile
FROM employees;
3. 偏移函数
sql 复制代码
-- LAG/LEAD: 访问前后行数据
SELECT 
    month,
    sales,
    LAG(sales, 1) OVER (ORDER BY month) AS prev_month_sales,
    LEAD(sales, 1) OVER (ORDER BY month) AS next_month_sales,
    sales - LAG(sales, 1) OVER (ORDER BY month) AS month_over_month_change
FROM monthly_sales;

-- FIRST_VALUE/LAST_VALUE: 获取窗口内第一个/最后一个值
SELECT 
    emp_name,
    dept_id,
    salary,
    FIRST_VALUE(emp_name) OVER (PARTITION BY dept_id ORDER BY salary DESC) AS highest_paid,
    LAST_VALUE(emp_name) OVER (
        PARTITION BY dept_id 
        ORDER BY salary DESC
        RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS lowest_paid
FROM employees;

四、MySQL常用内置函数

1. 时间日期函数

sql 复制代码
-- 获取当前时间
SELECT NOW(), CURDATE(), CURTIME();

-- 日期格式化
SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日 %H:%i:%s');

-- 日期计算
SELECT 
    DATE_ADD(NOW(), INTERVAL 7 DAY) AS '7天后',
    DATE_SUB(NOW(), INTERVAL 1 MONTH) AS '1个月前',
    DATEDIFF('2024-12-31', NOW()) AS '距离年底天数',
    TIMESTAMPDIFF(YEAR, '1990-01-01', NOW()) AS '年龄';

-- 提取日期部分
SELECT 
    YEAR(NOW()) AS '年',
    MONTH(NOW()) AS '月',
    DAY(NOW()) AS '日',
    DAYNAME(NOW()) AS '星期',
    WEEKDAY(NOW()) AS '星期索引';

2. 字符串函数

sql 复制代码
-- 字符串连接
SELECT CONCAT('Hello', ' ', 'World');
SELECT CONCAT_WS(',', 'A', 'B', 'C'); -- 使用分隔符

-- 字符串长度和位置
SELECT LENGTH('你好'), CHAR_LENGTH('你好'); -- 字节长度vs字符长度
SELECT LOCATE('world', 'hello world'); -- 查找位置

-- 字符串截取
SELECT 
    LEFT('abcdefg', 3) AS '左截取',
    RIGHT('abcdefg', 3) AS '右截取',
    SUBSTRING('abcdefg', 2, 3) AS '中间截取';

-- 字符串转换
SELECT 
    UPPER('hello') AS '大写',
    LOWER('HELLO') AS '小写',
    REPLACE('hello world', 'world', 'MySQL') AS '替换',
    TRIM('  hello  ') AS '去空格',
    REVERSE('hello') AS '反转';

3. 数学函数

sql 复制代码
-- 基础数学运算
SELECT 
    ABS(-10) AS '绝对值',
    CEIL(4.3) AS '向上取整',
    FLOOR(4.7) AS '向下取整',
    ROUND(4.567, 2) AS '四舍五入',
    TRUNCATE(4.567, 2) AS '截断';

-- 高级数学函数
SELECT 
    POW(2, 3) AS '幂运算',
    SQRT(16) AS '平方根',
    MOD(10, 3) AS '取余',
    RAND() AS '随机数',
    FLOOR(RAND() * 100) AS '0-99随机整数';

五、CASE WHEN条件判断

简单CASE语法

sql 复制代码
SELECT 
    emp_name,
    salary,
    CASE dept_id
        WHEN 1 THEN '技术部'
        WHEN 2 THEN '销售部'
        WHEN 3 THEN '人事部'
        ELSE '其他部门'
    END AS dept_name
FROM employees;

搜索CASE语法

sql 复制代码
-- 工资等级划分
SELECT 
    emp_name,
    salary,
    CASE 
        WHEN salary >= 20000 THEN '高级'
        WHEN salary >= 10000 THEN '中级'
        WHEN salary >= 5000 THEN '初级'
        ELSE '实习'
    END AS salary_level
FROM employees;

-- 条件统计
SELECT 
    dept_id,
    COUNT(CASE WHEN salary >= 10000 THEN 1 END) AS high_salary_count,
    COUNT(CASE WHEN salary < 10000 THEN 1 END) AS low_salary_count,
    SUM(CASE WHEN gender = '男' THEN salary ELSE 0 END) AS male_total_salary,
    SUM(CASE WHEN gender = '女' THEN salary ELSE 0 END) AS female_total_salary
FROM employees
GROUP BY dept_id;

-- 动态排序
SELECT * FROM employees
ORDER BY 
    CASE WHEN @sort_type = 'name' THEN emp_name END,
    CASE WHEN @sort_type = 'salary' THEN salary END DESC;

六、事务概念及应用

事务的概念

事务是一组不可分割的操作单元,要么全部成功,要么全部失败。

ACID特性

  • 原子性(Atomicity):事务是不可分割的最小操作单元
  • 一致性(Consistency):事务完成后,数据必须处于一致状态
  • 隔离性(Isolation):多个事务之间相互独立
  • 持久性(Durability):事务一旦提交,改变是永久的

事务操作

sql 复制代码
-- 开启事务
START TRANSACTION; -- 或 BEGIN;

-- 执行SQL操作
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;

-- 提交或回滚
COMMIT; -- 提交事务
ROLLBACK; -- 回滚事务

-- 设置保存点
SAVEPOINT point1;
-- 回滚到保存点
ROLLBACK TO point1;

事务隔离级别

sql 复制代码
-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;   -- 读已提交
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;  -- 可重复读(MySQL默认)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;      -- 串行化

应用场景示例

sql 复制代码
-- 转账操作
DELIMITER $$
CREATE PROCEDURE transfer_money(
    IN from_account INT,
    IN to_account INT,
    IN amount DECIMAL(10,2)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SELECT 'Transaction failed, rolled back' AS message;
    END;
    
    START TRANSACTION;
    
    UPDATE accounts SET balance = balance - amount 
    WHERE account_id = from_account AND balance >= amount;
    
    IF ROW_COUNT() = 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
    END IF;
    
    UPDATE accounts SET balance = balance + amount 
    WHERE account_id = to_account;
    
    COMMIT;
    SELECT 'Transaction successful' AS message;
END$$
DELIMITER ;

七、索引概念及应用

索引的概念

索引是帮助MySQL高效获取数据的数据结构,类似于书的目录。

索引类型

  1. 主键索引(PRIMARY KEY):唯一且不为NULL
  2. 唯一索引(UNIQUE):唯一,可以为NULL
  3. 普通索引(INDEX):最基本的索引
  4. 全文索引(FULLTEXT):用于全文搜索
  5. 组合索引:多列组成的索引

索引操作

sql 复制代码
-- 创建索引
CREATE INDEX idx_emp_name ON employees(emp_name);
CREATE UNIQUE INDEX idx_emp_email ON employees(email);
CREATE INDEX idx_emp_dept_salary ON employees(dept_id, salary); -- 组合索引

-- 查看索引
SHOW INDEX FROM employees;

-- 删除索引
DROP INDEX idx_emp_name ON employees;

-- 使用EXPLAIN分析查询
EXPLAIN SELECT * FROM employees WHERE emp_name = '张三';

索引使用原则

sql 复制代码
-- 1. 最左前缀原则(组合索引)
-- 假设有索引 (a, b, c)
SELECT * FROM table WHERE a = 1; -- 使用索引
SELECT * FROM table WHERE a = 1 AND b = 2; -- 使用索引
SELECT * FROM table WHERE b = 2; -- 不使用索引

-- 2. 避免索引失效的情况
-- 函数操作
SELECT * FROM employees WHERE YEAR(hire_date) = 2024; -- 索引失效
SELECT * FROM employees WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01'; -- 使用索引

-- 类型不匹配
SELECT * FROM employees WHERE emp_id = '123'; -- 如果emp_id是整数,可能失效

-- LIKE通配符
SELECT * FROM employees WHERE emp_name LIKE '%张%'; -- 索引失效
SELECT * FROM employees WHERE emp_name LIKE '张%'; -- 使用索引

索引优化建议

  • 选择性高的列适合建索引
  • 频繁作为查询条件的列建索引
  • 经常需要排序的列建索引
  • 避免过多索引,影响写入性能
  • 定期分析和优化索引

八、视图概念及应用

视图的概念

视图是一个虚拟表,其内容由查询定义。视图不存储数据,只存储SQL查询语句。

视图的优点

  1. 简化复杂查询
  2. 数据安全性(隐藏敏感列)
  3. 逻辑数据独立性

视图操作

sql 复制代码
-- 创建视图
CREATE VIEW v_emp_dept AS
SELECT 
    e.emp_id,
    e.emp_name,
    e.salary,
    d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

-- 使用视图
SELECT * FROM v_emp_dept WHERE salary > 10000;

-- 创建可更新视图
CREATE VIEW v_emp_simple AS
SELECT emp_id, emp_name, salary
FROM employees
WHERE dept_id = 1
WITH CHECK OPTION; -- 确保通过视图的修改符合WHERE条件

-- 修改视图
ALTER VIEW v_emp_dept AS
SELECT 
    e.emp_id,
    e.emp_name,
    e.salary,
    e.hire_date,
    d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

-- 删除视图
DROP VIEW IF EXISTS v_emp_dept;

视图应用场景

sql 复制代码
-- 1. 权限控制:为不同用户创建不同视图
CREATE VIEW v_emp_public AS
SELECT emp_id, emp_name, dept_id
FROM employees; -- 隐藏工资信息

-- 2. 简化复杂查询
CREATE VIEW v_sales_summary AS
SELECT 
    DATE_FORMAT(order_date, '%Y-%m') AS month,
    SUM(amount) AS total_sales,
    COUNT(DISTINCT customer_id) AS customer_count,
    AVG(amount) AS avg_order_value
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m');

-- 3. 计算字段
CREATE VIEW v_emp_annual AS
SELECT 
    emp_id,
    emp_name,
    salary,
    salary * 12 AS annual_salary,
    salary * 0.1 AS bonus
FROM employees;

九、ER模型和3NF三范式

ER模型(实体-关系模型)

基本概念
  • 实体(Entity):现实世界中的对象,如学生、课程
  • 属性(Attribute):实体的特征,如学生的姓名、年龄
  • 关系(Relationship):实体之间的联系
关系类型
  1. 一对一(1:1):一个实体对应另一个实体的一个实例
  2. 一对多(1:N):一个实体对应另一个实体的多个实例
  3. 多对多(M:N):多个实体对应另一个实体的多个实例
ER图示例
sql 复制代码
-- 学生与课程的多对多关系
-- 学生表
CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(50),
    age INT
);

-- 课程表
CREATE TABLE courses (
    course_id INT PRIMARY KEY,
    course_name VARCHAR(100),
    credits INT
);

-- 选课表(关系表)
CREATE TABLE enrollments (
    student_id INT,
    course_id INT,
    grade DECIMAL(3,1),
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES students(student_id),
    FOREIGN KEY (course_id) REFERENCES courses(course_id)
);

三范式(3NF)

第一范式(1NF):原子性

每个字段都是不可分割的原子值。

sql 复制代码
-- 违反1NF的设计
CREATE TABLE bad_students (
    student_id INT,
    student_name VARCHAR(50),
    phone_numbers VARCHAR(200) -- 存储多个电话号码,如"13812345678,13987654321"
);

-- 符合1NF的设计
CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(50)
);

CREATE TABLE student_phones (
    student_id INT,
    phone_number VARCHAR(20),
    phone_type VARCHAR(20),
    PRIMARY KEY (student_id, phone_number),
    FOREIGN KEY (student_id) REFERENCES students(student_id)
);
第二范式(2NF):完全依赖主键

非主键字段必须完全依赖于主键,不能只依赖主键的一部分。

sql 复制代码
-- 违反2NF的设计
CREATE TABLE bad_order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    product_name VARCHAR(100), -- 只依赖于product_id,不依赖于order_id
    product_price DECIMAL(10,2), -- 只依赖于product_id
    PRIMARY KEY (order_id, product_id)
);

-- 符合2NF的设计
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100),
    product_price DECIMAL(10,2)
);

CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);
第三范式(3NF):消除传递依赖

非主键字段之间不能有依赖关系,都应该直接依赖于主键。

sql 复制代码
-- 违反3NF的设计
CREATE TABLE bad_employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    dept_id INT,
    dept_name VARCHAR(50), -- 传递依赖:dept_name依赖于dept_id,而非直接依赖于emp_id
    dept_location VARCHAR(100) -- 传递依赖
);

-- 符合3NF的设计
CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(50),
    dept_location VARCHAR(100)
);

CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    dept_id INT,
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);

范式化的优缺点

优点
  • 减少数据冗余
  • 保证数据一致性
  • 节省存储空间
  • 更新异常少
缺点
  • 查询时需要更多JOIN操作
  • 可能影响查询性能
反范式化场景

在某些情况下,为了提高查询性能,可能会适度违反范式:

sql 复制代码
-- 适度冗余以提高查询性能
CREATE TABLE order_summary (
    order_id INT PRIMARY KEY,
    order_date DATE,
    customer_name VARCHAR(100), -- 冗余字段,避免每次都JOIN
    total_amount DECIMAL(10,2), -- 计算字段,避免每次都SUM
    item_count INT -- 统计字段
);

实战练习题

综合练习:电商数据分析

sql 复制代码
-- 创建示例表
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10,2)
);

-- 1. 使用窗口函数计算客户的购买排名
SELECT 
    customer_id,
    SUM(total_amount) AS total_purchase,
    RANK() OVER (ORDER BY SUM(total_amount) DESC) AS customer_rank
FROM orders
GROUP BY customer_id;

-- 2. 使用子查询找出消费超过平均值的客户
SELECT DISTINCT customer_id
FROM orders o1
WHERE (
    SELECT SUM(total_amount) 
    FROM orders o2 
    WHERE o2.customer_id = o1.customer_id
) > (
    SELECT AVG(customer_total) 
    FROM (
        SELECT SUM(total_amount) AS customer_total 
        FROM orders 
        GROUP BY customer_id
    ) t
);

-- 3. 使用CASE WHEN进行客户分类
SELECT 
    customer_id,
    SUM(total_amount) AS total_amount,
    CASE 
        WHEN SUM(total_amount) >= 10000 THEN 'VIP客户'
        WHEN SUM(total_amount) >= 5000 THEN '重要客户'
        WHEN SUM(total_amount) >= 1000 THEN '普通客户'
        ELSE '潜在客户'
    END AS customer_level
FROM orders
GROUP BY customer_id;

总结

掌握这些MySQL高级特性对于数据库开发和优化至关重要:

  1. 自关联查询子查询提供了灵活的数据查询方式
  2. 窗口函数让复杂的分析查询变得简单高效
  3. 内置函数CASE WHEN增强了SQL的表达能力
  4. 事务确保了数据的完整性和一致性
  5. 索引是提升查询性能的关键
  6. 视图简化了复杂查询并提供了安全性
  7. ER模型范式化是良好数据库设计的基础

建议通过大量实践来深入理解这些概念,并在实际项目中灵活运用。

相关推荐
萧曵 丶17 小时前
事务ACID特性详解
数据库·事务·acid
kejiayuan17 小时前
CTE更易懂的SQL风格
数据库·sql
kaico201817 小时前
MySQL的索引
数据库·mysql
清水白石00818 小时前
解构异步编程的两种哲学:从 asyncio 到 Trio,理解 Nursery 的魔力
运维·服务器·数据库·python
资生算法程序员_畅想家_剑魔18 小时前
Mysql常见报错解决分享-01-Invalid escape character in string.
数据库·mysql
PyHaVolask19 小时前
SQL注入漏洞原理
数据库·sql
ptc学习者19 小时前
黑格尔时代后崩解的辩证法
数据库
代码游侠19 小时前
应用——智能配电箱监控系统
linux·服务器·数据库·笔记·算法·sqlite
!chen19 小时前
EF Core自定义映射PostgreSQL原生函数
数据库·postgresql
霖霖总总19 小时前
[小技巧14]MySQL 8.0 系统变量设置全解析:SET GLOBAL、SET PERSIST 与 SET PERSIST_ONLY 的区别与应用
数据库·mysql