MySQL索引与视图综合应用示例解析

一、索引

1.索引是什么

  • 定义:索引是数据的"目录",用于快速定位数据、提升查询速度(以空间换时间)。
  • 核心作用:加速WHREEJOINORDER BY等操作,通过索引可以跳过全表扫描,显著提升查询效率。

2.索引的类型

索引类型 特点 示例
主键索引 唯一且非空,表只能有一个 PRIMARY KEY (user_id)
唯一索引 列值唯一,允许 NULL UNIQUE INDEX (email)
普通索引 加速查询,允许重复值 INDEX (username)
全文索引 针对文本内容的全文搜索(仅 InnoDB 支持) FULLTEXT (content)
组合索引 多列组合索引,遵循最左前缀原则 INDEX (last_name, first_name)

3.索引的创建

SQL 复制代码
-- 创建普通索引
CREATE INDEX idx_username ON users(username);

-- 创建唯一索引
CREATE UNIQUE INDEX idx_username on users(username);

-- 创建组合索引
CREATE INDEX idx_name ON users(last_name,first_name);

4.索引的注意事项

  • 不要过度索引:索引占用空间,且增删改查操作需要维护索引,影响写入性能

  • 选择合适的列:高频查询条件、JOIN字段、排序字段优先创建索引

  • 避免索引失效:

    • 对索引列使用函数或运算

    • 使用LIKE '%abc'

    • 类型不匹配

二、视图

1.视图是什么

  • 定义 :视图是虚拟表,基于SQL查询结果动态生成,不存储实际数据
  • 核心作用
    • 简化复杂查询(如多表联查)
    • 数据权限控制(隐藏敏感字段)提高数据安全性
    • 逻辑数据抽象(统计视图) 逻辑数据独立性

2.视图的操作

  • 创建不同的视图(3种)
SQL 复制代码
-- 创建简单的视图
CREATE VIEW v_active_users AS
SELECT id,name,email FROM users WHERE status = 'active';

-- 创建带有参数的视图
CREATE VIEW v_user_by_age(min_age) AS
SELECT id,name FROM users WHERE age>min_age;

-- 创建嵌套视图
CREATE VIEW v_summary AS
SELECT department, COUNT(*) AS staff_num 
FROM v_active_users 
GROUP BY department;
  • 查询视图

    SQL 复制代码
    SELECT * FROM v_active_users;
  • 修改视图

    SQL 复制代码
    CREATE OR REPLACE VIEW v_active_users AS
    SELECT id, name, email, phone FROM users WHERE status = 'active';
  • 删除视图

    SQL 复制代码
    DROP VIEW v_active_users;

3.视图的使用场景

  • 隐藏敏感字段(如工资)
  • 提供定制化数据视图
  • 简化多表关联查询
  • 实现权限控制

4.视图的注意事项

  • 简单视图性能直接接近查询
  • 复杂视图(含多表JOIN,子查询)性能较低
  • 可通过EXPLAN分析视图执行计划

三、综合实例

场景:员工管理系统 ,包含employees 表departments表

1.表结构

  • employees表 员工表

    字段名 类型 主键 外键 约束 说明
    employee_id INT ✔️ 自增 员工唯一标识
    first_name VARCHAR(50) NOT NULL 员工名字
    last_name VARCHAR(50) NOT NULL 员工姓氏
    age INT 年龄
    position VARCHAR(100) 职位(如:工程师)
    department_id INT ✔️ 所属部门ID(外键)
    phone CHAR(11) 手机号
    hire_date DATE 入职日期
    salary DECIMAL(10,2) 薪资
  • departments表

    字段名 类型 主键 说明
    department_id INT ✔️ 部门唯一标识
    department_name VARCHAR(100) NOT NULL 部门名称(如:技术部)

2.创建表

SQL 复制代码
-- 创建departments表 部门表
CREATE TABLE departments (
    department_id INT AUTO_INCREMENT PRIMARY KEY,
    department_name VARCHAR(100) NOT NULL
);

-- 创建employees表 员工表
CREATE TABLE employees (
	employee_id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    age INT,
    position VARCHAR(100),
   	phone VARCHAR(11) NOT NULL COMMENT '手机号',
    department_id INT,
    
    FOREIGN KEY (department_id) REFERENCES departments(department_id),
    CONSTRAINT chk_phone_format check (phone REGEXP '^[0-9]{11}$')
);
  • 修改表中的字段类型和添加列(添加了 薪资和入职时间)
SQL 复制代码
-- 修改表中的字段类型
ALTER TABLE employees
ADD COLUMN hire_date DATE,
ADD COLUMN salary DECIMAL(10,2),
MODIFY COLUMN phone CHAR(11);
  • 插入数据

    SQL 复制代码
    -- 插入部门数据
    INSERT INTO departments (department_name) VALUES
    ('技术部'),
    ('市场部'),
    ('人力资源部');
    
    -- 插入员工信息(23条数据)
    INSERT INTO employees (first_name,last_name,age,position,department_id,phone,hire_date,salary)
    VALUES 
    ('张三','伟',28,'高级工程师',1,'15138364800','2025-4-1',12000.00),
    ('李三','强',28,'产品经理',2,'15138364801','2025-3-20',18000.00),
    ('王二','狗',28,'HR专员',3,'15138364802','2025-2-17',11000.00),
    ('赵六','明',32,'技术总监',1,'15138364803','2025-1-15',25000.00),
    ('钱七','芳',29,'UI设计师',1,'15138364804','2024-12-22',15000.00),
    ('孙八','勇',35,'项目经理',1,'15138364805','2024-11-30',22000.00),
    ('周九','婷',27,'测试工程师',1,'15138364806','2024-10-18',13000.00),
    ('吴十','磊',31,'系统架构师',1,'15138364807','2024-9-5',28000.00),
    ('郑十一','敏',26,'前端开发',1,'15138364808','2024-8-12',16000.00),
    ('王十二','浩',33,'Java开发',1,'15138364809','2024-7-19',17000.00),
    ('冯十三','娜',30,'市场总监',2,'15138364810','2024-6-25',23000.00),
    ('陈十四','强',28,'市场分析师',2,'15138364811','2024-5-14',19000.00),
    ('褚十五','丽',34,'品牌经理',2,'15138364812','2024-4-3',20000.00),
    ('卫十六','飞',29,'市场专员',2,'15138364813','2024-3-21',12000.00),
    ('蒋十七','静',27,'公关主管',2,'15138364814','2024-2-28',14000.00),
    ('沈十八','伟',36,'人力资源总监',3,'15138364815','2024-1-16',35000.00),
    ('韩十九','娟',31,'薪酬经理',3,'15138364816','2023-12-24',18000.00),
    ('杨二十','刚',29,'招聘主管',3,'15138364817','2023-11-11',16000.00),
    ('朱廿一','琳',33,'培训经理',3,'15138364818','2023-10-9',21000.00),
    ('秦廿二','超',27,'员工关系专员',3,'15138364819','2023-9-7',11000.00),
    ('尤廿三','敏',30,'绩效主管',3,'15138364820','2023-8-5',15000.00),
    ('许廿四','强',35,'组织发展经理',3,'15138364821','2023-7-3',24000.00),
    ('何廿五','娜',28,'HRBP',3,'15138364822','2023-6-1',17000.00);

3.常规查询

  • 查询技术部所有员工信息 (包括部门名称,不显示部门ID)

    SQL 复制代码
    SELECT e.first_name,e.last_name,e.age,e.position,e.phone,e.hire_date,e.salary,d.department_name
    FROM employees e
    JOIN departments d ON e.department_id=d.department_id
    WHERE d.department_name = '技术部';
  • 统计各部门员工数量及平均薪资

    SQL 复制代码
    -- 方法1
    SELECT 
        d.department_name,
        (SELECT COUNT(*) 
         FROM employees e 
         WHERE e.department_id = d.department_id) AS employees_number,
        (SELECT AVG(salary) 
         FROM employees e 
         WHERE e.department_id = d.department_id) AS avg_salary
    FROM departments d;
    
    -- 方法2:
    SELECT 
    	d.department_name,
    	COUNT(e.employee_id) AS employees_number,
    	AVG(e.salary) AS avg_salary
    FROM departments d
    JOIN employees e ON e.department_id = d.department_id
    GROUP BY d.department_id, d.department_name;
  • 查找入职超过1年的员工

    SQL 复制代码
    -- 分析:只有入职的时间,我需要获取与当下的时间差
    SELECT 
    	first_name,
        last_name,
        hire_date,
        DATEDIFF(CURDATE(), hire_date) AS days_since_hire
    FROM employees
    WHERE DATEDIFF(CURDATE(), hire_date) > 365;

4.数据维护操作

  • 更新员工部门

    SQL 复制代码
    UPDATE employees
    SET department_id = '2'
    WHERE employee_id = 1; -- 将张三调整到市场部门
  • 删除部门及其关联员工

    SQL 复制代码
    -- 先删除部门(需要处理外键约束)
    DELETE FROM departments
    WHERE department_id = 3; --删除人力资源部
    
    -- 使用级联删除(我们先开始定义表的时候,没有设置级联删除)
    
    -- 删除
    ALTER TABLE employees DROP CONSTRAINT employees_ibfk_1;-- 删除约束
    
    -- 删除+重建
    ALTER TABLE employees
    DROP CONSTRAINT employees_ibfk_1,
    ADD CONSTRAINT fk_department
    FOREIGN KEY (department_id) 
    REFERENCES departments(department_id)
    ON DELETE CASCADE;
    • 问题:我发现我在定义外键的时候没有定义外键名,系统为我们自动定义了外键名,需要自行查询

      SQl 复制代码
      SELECT CONSTRAINT_NAME
      FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -- 数据库的管理系统
      WHERE TABLE_NAME = 'employees'
      AND CONSTRAINT_TYPE = 'FOREIGN KEY';
      
      -- 输出结果:employees_ibfk_1

5.索引优化示例

  • 设置普通的索引

    SQL 复制代码
    -- 将部门id设置为索引
    CREATE INDEX idx_department ON employees(department_id);
    
    -- 将入职时间设置为索引
    CREATE INDEX idx_hire_date ON employees(hire_date);
    • 将department_id设置为索引的原因:

      • 查询频率:部门关联查询的核心字段 :部门人数,查询部门成员

      • 选择性原则:作为外键具有天然的高选择性

  • 设置复合索引优化部门查询

    SQL 复制代码
    CREATE INDEX idx_dept_salary
    ON employees(department_id, salary DESC)
    INCLUDE (first_name, last_name);  -- 覆盖索引,不参与排序结构
    SQL 复制代码
    -- 查找市场部薪资最高的10名员工
    SELECT TOP 10 
        first_name, 
        last_name, 
        salary 
    FROM employees
    WHERE department_id = (SELECT department_id FROM departments WHERE department_name = '市场部')
    ORDER BY salary DESC;
    • 使用复合索引 (department_id, salary DESC)查询流程

      • 索引定位

        • 根节点 开始,比较department_id=3 的范围
        • 快速导航到department_id的索引区域
      • 有序扫描

        • 在·department_id =3的范围内,数据已按salary DESC预排序
        • 直接从该部门薪资最高的记录开始顺序读取
      • 数据获取

        (覆盖索引)

        • 直接从索引子节点获取first_name,last_name,salary

        • 不需要访问主表(避免"回表"操作)

          (普通索引)

        • 通过索引找到主键/行指针

        • 回主表查找完整记录(额外的I/O操作)

      • 结果返回

        • 按顺序返回前10条记录
        • 由数据已排序,不需要临时排序操作
  • 函数索引优化日期查询

    SQL 复制代码
    CREATE INDEX idx_hire_year
    ON employees(YEAR(hire_date));
    SQL 复制代码
    -- 统计2024年入职员工数量
    SELECT COUNT(*)
    FROM employees
    WHERE YEAR(hire_date) = 2024;

6.视图示例

  • 部门员工统计视图

    SQL 复制代码
    CREATE VIEW department_employee_stats AS 
    SELECT 
    	d.department_name,
        COUNT(e.employee_id) AS employee_count,
        ROUND(AVG(e.salary), 2) AS avg_salary,
        MAX(e.hire_date) AS latest_hire_date
    FROM departments d
    LEFT JOIN employees e ON d.department_id = e.department_id
    GROUP BY d.department_name;
    SQL 复制代码
    -- 查看所有部门统计
    SELECT * FROM department_employee_stats;
     
    -- 筛选员工数超过50的部门
    SELECT * FROM department_employee_stats 
    WHERE employee_count > 50;
相关推荐
用户17592342150285 分钟前
D3.js - 基本用法
前端·d3.js
张风捷特烈13 分钟前
Flutter 伪 3D 绘制#02 | 地平面与透视
android·flutter
每次的天空19 分钟前
Kotlin 作用域函数:apply、let、run、with、also
android·开发语言·kotlin
Mr.Liu621 分钟前
小程序30-wxml语法-声明和绑定数据
前端·微信小程序·小程序
76756047922 分钟前
useDateFormat源码解析
前端·源码
Mintopia22 分钟前
Three.js粒子系统开发实战:从基础到性能优化
前端·javascript·three.js
Promise52023 分钟前
大屏"跑马灯" 长列表性能优化
前端·javascript
子玖23 分钟前
初始化项目前的准备
前端·javascript·vue.js
Mintopia23 分钟前
Three.js进阶实战:打造动态光影交互场景 ——结合环境光、聚光灯与相机控制的沉浸式体验
前端·javascript·three.js
贵州数擎科技有限公司24 分钟前
Threejs绘制小兩伞快拿去送给你的女神
前端