四、表关系与复杂查询
一、表关系设计与约束
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. 索引优化原则
-
最左前缀原则:联合索引
(A,B,C)可生效于:WHERE A=1WHERE A=1 AND B=2WHERE A=1 AND B=2 AND C=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 执行顺序
- 执行
FROM departments d - 执行
JOIN employees e→ 生成中间结果集 - 执行
LEFT JOIN projects p→ 扩展结果集 - 应用
WHERE过滤(如果有) - 执行
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,包含列nSELECT 1 -- 初始查询:从1开始UNION ALL -- 递归部分:将结果与后续查询合并SELECT n+1 FROM numbers -- 递归查询:每次将n的值加1WHERE 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), -- 产品价格,必须大于等于0stock 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), -- 购买数量,必须大于0PRIMARY 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会返回0COALESCE(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. 设计医院管理系统
- 核心表:患者表、医生表、科室表、预约记录表、病历表
- 要求:
- 实现多对多关系(医生-科室)
- 添加合理的约束(如预约时间不能早于当前时间)
- 创建必要的索引
- 编写以下查询:
- 查找某科室最繁忙的医生
- 统计各科室的预约成功率
- 检索患者的完整就诊历史
2. 性能优化挑战
- 对已有慢查询进行
EXPLAIN分析 - 通过索引优化将查询时间从 2s 降至 200ms 以内
- 使用
FORCE INDEX强制使用特定索引对比效果
相关文章:
四、表关系与复杂查询
一、表关系设计与约束 1. 表关系类型与实现 关系类型实现方式示例场景一对一共享主键 或 外键唯一约束用户 ↔ 用户详细信息一对多外键约束部门 ↔ 员工多对多中间表 联合主键学生 ↔ 课程 2. 核心约束类型 -- 完整表创建示例(含约束) CREATE TABLE…...
Qt 中,**信号与槽(Signals Slots)机制
在 Qt 中,信号与槽(Signals & Slots)机制 是实现对象间通信的核心模式,通常也被视为一种高效的“通知者模式”。它允许对象在特定事件发生时通知其他对象,且完全解耦。 核心概念 信号(Signal࿰…...
Javaweb后端数据库多表关系一对多,外键,一对一
多表关系 一对多 多的表里,要有一表里的主键 外键 多的表上,添加外键 一对一 多对多 案例...
使用Apache Lucene构建高效的全文搜索服务
使用Apache Lucene构建高效的全文搜索服务 在现代应用程序中,全文搜索功能是不可或缺的一部分。无论是电子商务网站、内容管理系统,还是数据分析平台,快速、准确地搜索大量数据是提升用户体验的关键。Apache Lucene 是一个强大的全文搜索引擎…...
VScode在Windows11中配置MSVC
因为MSVC编译器在vs当中,所以我们首先要安装vs的一部分组件。如果只是需要MSVC的话,工作负荷一个都不需要勾选,在单个组件里面搜索MSVC和windows11 SDK,其中一个是编译器,一个是头文件然后右下角安装即可。搜索Develop…...
【洛谷贪心算法题】P2240部分背包问题
【解题思路】 贪心策略选择 对于部分背包问题,关键在于如何选择物品放入背包以达到最大价值。由于物品可以分割,遍历排序后的物品数组,根据物品重量和背包剩余容量的关系,决定是将整个物品放入背包还是分割物品放入背包ÿ…...
DevOps原理和实现面试题及参考答案
解释 DevOps 的核心目标与文化价值观,如何理解 “CAMS” 模型? DevOps 的核心目标是打破开发(Development)和运维(Operations)之间的壁垒,通过自动化、协作和持续反馈,实现软件的快速、可靠交付,以更好地满足业务需求和客户期望。具体来说,DevOps 旨在缩短软件的交付…...
《Somewhat Practical Fully Homomorphic Encryption》笔记 (BFV 源于这篇文章)
文章目录 一、摘要二、引言1、FHE 一般分为三个逻辑部分2、噪声的管理3. 贡献点4. 文章思路 三、基础数学知识四、基于 RLWE 的加密1. LWE 问题2. RLWE 问题3. RLWE 问题的难度和安全性 五、加密方案1. LPR.ES 加密方案2. Lemma 1 (引理 1)3. Optimisation/Assumption 1 (优化/…...
SpringBoot 2 后端通用开发模板搭建(异常处理,请求响应)
目录 一、环境准备 二、新建项目 三、整合依赖 1、MyBatis Plus 数据库操作 2、Hutool 工具库 3、Knife4j 接口文档 4、其他依赖 四、通用基础代码 1、自定义异常 2、响应包装类 3、全局异常处理器 4、请求包装类 5、全局跨域配置 补充:设置新建类/接…...
DeepSeek本地部署与Dify结合创建私有知识库指南
python调用本地deepseek+Dify的API使用--测试WX自动发送信息-CSDN博客 DeepSeek,一家在人工智能领域具有显著技术实力的公司,凭借其千亿参数规模的AI大模型,以及仅需0.5元人民币即可进行百万tokens的API调用成本,已经取得了令人瞩目的成就。不仅如此,DeepSeek的模…...
Nginx 报错:413 Request Entity Too Large
做web开发时,对于上传附件的功能,如果nginx没有调整配置,上传大一点的文件就会发生下面这种错误: 要解决上面的问题,只需要调整Nginx配置文件中的 client_max_body_size 参数即可,这个配置参数一般在http配…...
Arduino项目实战:使用MQ-2气体传感器与OLED屏幕监测环境气体
概述 在这个项目中,MQ-2气体传感器是一个多功能的气体检测设备,能够感知多种常见气体,如甲烷、丁烷、丙烷、酒精和烟雾等。你可以把它想象成一个超级灵敏的“嗅觉”,能够帮助你实时检测环境中的各种有害气体。与Arduino板连接后,MQ-2传感器把捕捉到的气体浓度数据传送给A…...
泛微Ecode新增Button调用服务器中的JSP页面里的方法
前言 前端Ecode调用 后端接口编写 JSP文件方法 总结 前言 因为我们是从之前E8版本升级到E9的,所以会有一些接口是通过jsp文件来实现前后端调用的,这里介绍的就是如果你有接口是写在jsp文件里面调用的,但是你又想在Ecode中调用的对应的接…...
C#实现本地Deepseek模型及其他模型的对话
前言 1、C#实现本地AI聊天功能 WPFOllamaSharpe实现本地聊天功能,可以选择使用Deepseek 及其他模型。 2、此程序默认你已经安装好了Ollama。 在运行前需要线安装好Ollama,如何安装请自行搜索 Ollama下载地址: https://ollama.org.cn Ollama模型下载地址…...
【ESP32S3接入讯飞在线语音识别】
视频地址: 【ESP32S3接入讯飞在线语音识别】 1. 前言 使用Seeed XIAO ESP32S3 Sense开发板接入讯飞实现在线语音识别。自带麦克风模块用做语音输入,通过串口发送字符“1”来控制数据的采集和上传。 语音识别对比 平台api教程评分百度...
【51单片机】快速入门
动手实践 > 理论空谈!从点亮LED开始,逐步扩展功能,2周可入门基础。 一、51单片机基础概念 什么是51单片机? 基于Intel 8051架构的8位微控制器,广泛用于嵌入式开发。 核心特性:4KB ROM、128B RAM、32个…...
leetcode707----设计链表【链表增删改打印等操作】
目录 一、题目介绍 二、单链表 2.1 创建链表类 2.1.1 定义链表节点结构体代码块 2.1.2 MyLinkedList类的构造函数 2.1.3 私有成员变量 2.2 接口1:获取第下标为index的节点的值 2.3 接口2:头部插入节点 2.4 接口3:尾部插入节点 2.5 接…...
【问题记录】Go项目Docker中的consul访问主机8080端口被拒绝
【问题记录】Go项目Docker中的consul访问主机8080端口被拒绝 问题展示解决办法 问题展示 在使用docker中的consul服务的时候,通过命令行注册相应的服务(比如cloudwego项目的demo_proto以及user服务)失败。 解决办法 经过分析,是…...
【缓存】缓存雪崩与缓存穿透:高并发系统的隐形杀手
缓存雪崩与缓存穿透:高并发系统的隐形杀手 在高并发系统中,缓存是提升性能的重要手段。然而,缓存使用不当也会带来一系列问题,其中最常见的就是缓存雪崩和缓存穿透。这两个问题如果不加以解决,可能会导致系统崩溃&…...
网络协议 HTTP、HTTPS、HTTP/1.1、HTTP/2 对比分析
1. 基本定义 HTTP(HyperText Transfer Protocol) 应用层协议,用于客户端与服务器之间的数据传输(默认端口 80)。 HTTP/1.0:早期版本,每个请求需单独建立 TCP 连接,效率低。HTTP/1.1&…...
DeepSeek实现FunctionCalling调用API查询天气
什么是FunctionCalling Function Calling(函数调用)是大型语言模型(如 OpenAI 的 GPT 系列)提供的一种能力,允许模型在生成文本的过程中调用外部函数或工具,以完成更复杂的任务。通过 Function Calling&am…...
从 Spring Boot 2 升级到 Spring Boot 3 的终极指南
一、升级前的核心准备 1. JDK 版本升级 Spring Boot 3 强制要求 Java 17 及以上版本。若当前项目使用 Java 8 或 11,需按以下步骤操作: 安装 JDK 17:从 Oracle 或 OpenJDK 官网下载,配置环境变量(如 JAVA_HOME&…...
C#设计模式深度解析:经典实现与现代演进 ——基于《设计模式》的.NET技术实践
一、设计模式与C#语言特性融合 C#凭借其面向对象特性、泛型、委托/事件、LINQ等能力,为设计模式提供了更优雅的实现方式。以下通过典型模式展现其技术融合: 1. 工厂方法模式 泛型约束 public interface IProduct<T> where T : new() {void O…...
原子性(Atomicity)和一致性(Consistency)的区别?
原子性(Atomicity)和一致性(Consistency)是数据库事务ACID特性中的两个核心概念,虽然它们密切相关,但解决的问题和侧重点完全不同。原子性关注事务的操作完整性,而一致性关注数据的逻辑正确性。…...
windows设置暂停更新时长
windows设置暂停更新时长 win11与win10修改注册表操作一致 ,系统界面不同 1.打开注册表 2.在以下路径 \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 右键新建 DWORD 32位值,名称为FlightSettingsMaxPauseDays 根据需求填写数…...
【Kimi】自动生成PPT-并支持下载和在线编辑--全部免费
【Kimi】免费生成PPT并免费下载 用了好几个大模型,有些能生成PPT内容; 有些能生成PPT,但下载需要付费; 目前只有Kimi生成的PPT,能选择模板、能在线编辑、能下载,关键全部免费! 一、用kimi生成PP…...
一款在手机上制作电子表格
今天给大家分享一款在手机上制作电子表格的,免费好用的Exce1表格软件,让工作变得更加简单。 1 软件介绍 Exce1是一款手机制作表格的办公软件,您可以使用手机exce1在线制作表格、工资表、编辑xlsx和xls表格文件等,还可以学习使用…...
【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.3.1单节点安装(Docker与手动部署)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 10分钟快速部署Elasticsearch单节点环境1. 系统环境要求1.1 硬件配置推荐1.2 软件依赖 2. Docker部署方案2.1 部署流程2.2 参数说明2.3 性能优化建议 3. 手动部署方案3.1 安…...
7 天精通 DeepSeek 实操手册
挑战目标 从零基础开始,用 7 天时间,精通 DeepSeek 实操。 对零基础的同学来说,要全部完成这个挑战并不容易。因此,我们提供了每天的学习目标和实操任务,并提供三大锦囊助你一臂之力: 针对常见问题的解决…...
过滤器 二、过滤器详解
过滤器生命周期: init(FilterConfig):在服务器启动时会创建Filter实例,并且每个类型的Filter只创建一个实例,从此不再创建!在创建完Filter实例后,会马上调用init()方法完成初始化工作,这个方法…...
