当前位置: 首页 > news >正文

四、表关系与复杂查询

一、表关系设计与约束

1. 表关系类型与实现

关系类型实现方式示例场景
一对一共享主键 或 外键唯一约束用户 ↔ 用户详细信息
一对多外键约束部门 ↔ 员工
多对多中间表 + 联合主键学生 ↔ 课程

2. 核心约束类型

-- 完整表创建示例(含约束)
CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,      -- 主键约束
    emp_name VARCHAR(50) NOT NULL,              -- 非空约束
    email VARCHAR(100) UNIQUE,                  -- 唯一约束
    dept_id INT,
    salary DECIMAL(10,2) CHECK (salary > 0),    -- 检查约束(MySQL 8.0+)
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id) -- 外键约束
);
约束类型详解
约束类型作用注意事项
PRIMARY KEY唯一标识记录,自动创建聚集索引每表只能有一个主键
FOREIGN KEY强制引用完整性可能影响性能,高并发慎用
UNIQUE保证字段值唯一可空字段允许存在多个NULL值
CHECK自定义验证规则MySQL 8.0前需通过触发器实现
NOT NULL禁止空值与DEFAULT配合使用更安全

二、索引与查询优化

1. 索引的作用与类型

索引类型特点适用场景
B-Tree索引默认索引,支持范围查询大多数场景(如WHERE、ORDER BY)
唯一索引强制字段值唯一身份证号、邮箱等字段
联合索引多列组合索引常一起查询的字段组合

2. 索引创建与使用

-- 创建索引
CREATE INDEX idx_emp_name ON employees(emp_name);  -- 单列索引
CREATE UNIQUE INDEX idx_email ON employees(email);-- 唯一索引
CREATE INDEX idx_dept_salary ON employees(dept_id, salary); -- 联合索引

-- 查看索引
SHOW INDEX FROM employees;

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

3. 索引优化原则

  1. 最左前缀原则:联合索引 (A,B,C) 可生效于:

    • WHERE A=1
    • WHERE A=1 AND B=2
    • WHERE A=1 AND B=2 AND C=3
  2. 避免过度索引:索引会降低写操作速度

  3. 覆盖索引:查询字段全部在索引中时,无需回表

    -- 使用覆盖索引
    SELECT emp_name FROM employees WHERE emp_name LIKE '张%';
    

三、高级关联查询

1. JOIN 执行原理

-- 示例:三表关联
-- 查询部门名称、员工姓名及其领导的项目名称
SELECT 
    d.dept_name,        -- 选择部门名称
    e.emp_name,         -- 选择员工姓名
    p.project_name      -- 选择项目名称
FROM departments d      -- 从部门表开始查询
JOIN employees e        -- 连接员工表
    ON d.dept_id = e.dept_id  -- 连接条件:部门ID匹配
LEFT JOIN projects p    -- 左连接项目表
    ON e.emp_id = p.leader_id;  -- 连接条件:员工ID与项目领导ID匹配
JOIN 执行顺序
  1. 执行 FROM departments d
  2. 执行 JOIN employees e → 生成中间结果集
  3. 执行 LEFT JOIN projects p → 扩展结果集
  4. 应用 WHERE 过滤(如果有)
  5. 执行 SELECT 字段投影

2. 自连接(Self Join)

-- 查询在同一部门中的不同员工对
SELECT 
    e1.emp_name AS employee1,  -- 选择第一个员工的姓名
    e2.emp_name AS employee2   -- 选择第二个员工的姓名
FROM employees e1              -- 从员工表(别名 e1)开始查询
JOIN employees e2              -- 自连接员工表(别名 e2)
  ON e1.dept_id = e2.dept_id   -- 连接条件:部门ID相同
  AND e1.emp_id < e2.emp_id;   -- 确保员工ID不同且避免重复配对

3. 递归查询(WITH RECURSIVE)

-- 使用递归CTE生成从1到10的数字序列
WITH RECURSIVE numbers(n) AS (  -- 定义递归CTE,命名为numbers,包含列n
    SELECT 1                    -- 初始查询:从1开始
    UNION ALL                   -- 递归部分:将结果与后续查询合并
    SELECT n+1 FROM numbers     -- 递归查询:每次将n的值加1
    WHERE n < 10                -- 递归终止条件:n小于10时继续递归
)
SELECT * FROM numbers;          -- 最终查询:返回递归CTE的结果

四、综合案例:电商系统优化

1. 带约束的表设计

--产品表(products)
CREATE TABLE products (
    product_id INT PRIMARY KEY AUTO_INCREMENT,  -- 产品ID,主键,自增
    product_name VARCHAR(255) NOT NULL,    -- 产品名称,必填
    price DECIMAL(10,2) CHECK (price >= 0),    -- 产品价格,必须大于等于0
    stock INT DEFAULT 0 CHECK (stock >= 0)    -- 库存数量,默认值为0,必须大于等于0
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY AUTO_INCREMENT,    -- 订单ID,主键,自增
    user_id INT NOT NULL,    -- 用户ID,必填,外键关联用户表
    order_date DATETIME DEFAULT CURRENT_TIMESTAMP,    -- 订单日期,默认值为当前时间
    status ENUM('pending', 'shipped', 'completed') DEFAULT 'pending',    -- 订单状态,枚举类型,默认值为'pending'
    FOREIGN KEY (user_id) REFERENCES users(user_id)    -- 外键约束,关联用户表的user_id字段
);

-- 中间表(含联合主键)
CREATE TABLE order_items (
    order_id INT,    -- 订单ID,联合主键之一,外键关联订单表
    product_id INT,    -- 产品ID,联合主键之一,外键关联产品表
    quantity INT CHECK (quantity > 0),    -- 购买数量,必须大于0
    PRIMARY KEY (order_id, product_id),    -- 联合主键,由order_id和product_id组成
    FOREIGN KEY (order_id) REFERENCES orders(order_id),    -- 外键约束,关联订单表的order_id字段
    FOREIGN KEY (product_id) REFERENCES products(product_id)    -- 外键约束,关联产品表的product_id字段
);

2. 索引优化实战

-- 高频查询:按用户和状态查订单
CREATE INDEX idx_user_status ON orders(user_id, status);

-- 高频搜索:商品名称模糊查询
CREATE FULLTEXT INDEX idx_product_name ON products(product_name);

-- 使用全文索引查询
SELECT * FROM products 
WHERE MATCH(product_name) AGAINST('手机 -配件' IN BOOLEAN MODE);

3. 复杂业务查询

-- 统计每个用户的2023年度消费总金额(包含未消费用户)
SELECT 
    u.user_name,                             -- 用户姓名
    --当SUM的结果为NULL时,COALESCE会返回0
    COALESCE(SUM(oi.quantity * p.price), 0) AS total_spent  -- 计算消费总金额(未消费显示0)
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id          -- 关联用户和订单表(保留未下单用户)
LEFT JOIN order_items oi ON o.order_id = oi.order_id  -- 关联订单和订单明细表
LEFT JOIN products p ON oi.product_id = p.product_id  -- 关联订单明细和商品表
WHERE o.order_date BETWEEN '2023-01-01' AND '2023-12-31'  -- 筛选2023年订单
GROUP BY u.user_id;                          -- 按用户ID分组汇总消费金额

-- 查找库存紧张的热销商品(销量TOP 10且库存<100)
SELECT 
    p.product_name,                         -- 商品名称
    SUM(oi.quantity) AS total_sold,         -- 统计商品总销量
    p.stock                                 -- 显示当前库存量
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id  -- 关联商品和订单明细表
GROUP BY p.product_id                       -- 按商品ID分组统计销量
HAVING total_sold > 100 AND stock < 100     -- 筛选销量>100且库存<100的商品
ORDER BY total_sold DESC                    -- 按销量降序排列
LIMIT 10;                                   -- 仅显示销量最高的前10条记录

五、常见错误与调试技巧

1. 外键约束冲突

错误示例

-- 尝试删除有子记录的部门
DELETE FROM departments WHERE dept_id = 1;
-- 报错:Cannot delete or update a parent row: a foreign key constraint fails

解决方案

-- 方法1:先删除子记录
DELETE FROM employees WHERE dept_id = 1;
DELETE FROM departments WHERE dept_id = 1;

-- 方法2:使用级联删除(设计表时定义)
CREATE TABLE employees (
    ...
    FOREIGN KEY (dept_id) 
      REFERENCES departments(dept_id)
      ON DELETE CASCADE
);

2. 索引失效场景

场景示例优化方案
对索引列使用函数WHERE YEAR(create_time)=2023改为范围查询:WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
使用前导通配符模糊查询WHERE name LIKE '%张三%'使用全文索引或倒序存储
隐式类型转换WHERE id = '100'(id为INT)保持类型一致:WHERE id = 100

六、实战练习建议

1. 设计医院管理系统

  • 核心表:患者表、医生表、科室表、预约记录表、病历表
  • 要求
    1. 实现多对多关系(医生-科室)
    2. 添加合理的约束(如预约时间不能早于当前时间)
    3. 创建必要的索引
    4. 编写以下查询:
      • 查找某科室最繁忙的医生
      • 统计各科室的预约成功率
      • 检索患者的完整就诊历史

2. 性能优化挑战

  1. 对已有慢查询进行 EXPLAIN 分析
  2. 通过索引优化将查询时间从 2s 降至 200ms 以内
  3. 使用 FORCE INDEX 强制使用特定索引对比效果

相关文章:

  • 【Python修仙编程】(二) Python3灵源初探(3)
  • MySQL—密码设置相关
  • VAE变分自编码器的初步理解
  • 零基础转行机器视觉,应该学halcon,opencv,海康visionmaster,康耐视visionpro哪一个好
  • DDS技术分享(二)---IDL
  • [ISP] AE 自动曝光
  • x64汇编下过程参数解析
  • 性能测试的脚本录制
  • Redis除了做缓存还能做什么?
  • springai系列(一):接入deepseek问答,扫坑记录
  • 猿大师播放器:HTML内嵌VLC播放RTSP视频流,无需转码,300ms级延迟,碾压服务器转码方案
  • 团队协作中的分支合并:构建高效开发流程的关键
  • 【笔记】论文阅读方法(AI大模型)
  • 半导体晶圆制造良率提升的指标体系设计
  • mapbox基础,使用geojson加载heatmap热力图层
  • 阿里巴巴 item_search 按关键字搜索商品开发应用指南
  • MOBA:长上下文 LLMs 的混合块注意机制
  • SOME/IP-SD -- 协议英文原文讲解7
  • 几个api
  • Windows 图形显示驱动开发-WDDM 3.2-自动显示切换(十)
  • 证监会披露两起操纵市场处罚结果,今年来涉操纵股票罚没金额超7.5亿元
  • 江南考古文脉探寻
  • 新任重庆市垫江县委副书记刘振已任县政府党组书记
  • 美国新泽西客运公司遭遇罢工:40年来首次,35万人受影响
  • 巴菲特最新调仓:一季度大幅抛售银行股,再现保密仓位
  • 悬疑剧背后的女编剧:创作的差异不在性别,而在经验