MySql安装及SQL语句

安装

下载安装

下一步等待安装

下一步

连接数据库协议以及接口,默认保持不动

密码强度

设置密码

默认保持不变

简介

MySQL 是一款广泛应用的开源关系型数据库管理系统(RDBMS),由瑞典 MySQL AB 公司开发,后来被甲骨文(Oracle)公司收购。它以其高性能、可靠性和易用性,成为了 Web 应用程序开发中的首选数据库之一。

特点

  1. 开源免费:MySQL 遵循开源许可协议,用户可以自由地使用、修改和分发,这大大降低了企业和开发者的成本。

  2. 高性能:经过优化的 MySQL 能够处理大量数据和高并发访问,在各种硬件环境下都能提供出色的性能表现。它采用了高效的查询优化器和索引机制,能够快速响应用户的查询请求。

  3. 可靠性:MySQL 具备完善的数据备份和恢复机制,支持事务处理和数据完整性约束,确保数据的安全性和一致性。即使在面对意外情况时,也能最大程度地保证数据的完整性。

  4. 易用性:MySQL 提供了简单易懂的 SQL 接口,方便开发者进行数据库的创建、查询、更新和删除操作。同时,它还拥有丰富的文档和社区支持,初学者可以快速上手。

  5. 跨平台性:MySQL 可以在多种操作系统上运行,如 Linux、Windows、Mac OS 等,具有良好的跨平台兼容性。这使得开发者可以根据自己的需求选择合适的操作系统进行开发和部署。

  6. 扩展性:MySQL 支持多种存储引擎,如 InnoDB、MyISAM 等,用户可以根据不同的应用场景选择合适的存储引擎。此外,它还支持集群和分区技术,能够轻松应对大规模数据的存储和处理需求。

在初次安装 MySQL 后,默认会存在几个系统数据库,包括 sys mysql information_schema performance_schema

1. sys 数据库

  • 作用

    • 是 MySQL 5.7+ 版本引入的系统数据库。
    • 提供了一系列预定义的视图和存储过程,用于简化对 performance_schemainformation_schema 的查询。
    • 主要用于性能分析和故障排查(例如查看锁、I/O 负载、内存使用等)。
  • 能否删除

    • 可以删除,但通常不建议这样做。
    • 删除后可能会影响某些性能分析工具的功能。
    • 如果需要恢复,可以通过执行 mysql_sys_schema.sql 脚本重新创建(位于 MySQL 安装目录的 scripts 文件夹)。

2. mysql 数据库

  • 作用

    • 存储 MySQL 的核心数据,包括用户权限、存储过程、事件、时区信息等。
    • 包含 userdbtables_priv 等关键系统表。
  • 能否删除

    • 绝对不能删除!删除会导致 MySQL 服务无法正常运行,甚至崩溃。

3. information_schema 数据库

  • 作用

    • 提供对数据库元数据的访问(如表、列、索引、权限等信息)。
    • 是 ANSI SQL 标准的一部分,所有数据均为只读视图。
  • 能否删除

    • 不能删除,也无法删除。它是 MySQL 内部实现的虚拟数据库。

4. performance_schema 数据库

  • 作用

    • 收集 MySQL 服务器的运行时性能数据(如锁、线程、内存使用等)。
    • 用于监控和优化数据库性能。
  • 能否删除

    • 不能删除 ,但可以通过配置参数关闭其功能(设置 performance_schema=OFF)。

SQL语句

SQL是Structured Query Language ,称之为结构化查询语言,简称SQL

使用SQL****编写出来的语句 ,就称之为SQL语句

SQL语句可以用于对数据库进行操作

SQL语句的常用规范:

  • 通常关键字使用大写的,比如CREATE、TABLE、SHOW等等;

  • 一条语句结束后,需要以;结尾;

  • 如果遇到关键字作为表明或者字段名称,可以使用``包裹;

数据类型

一、数字类型

  1. 整数类型

    1. TINYINT:1 字节,范围 -128~1270~255(无符号)。 用途 :状态标记(如 0/1 表示布尔值)。
    2. INT:4 字节,范围 -2^31 ~ 2^31-1最常用的主键、计数器等。
    3. BIGINT:8 字节,超大范围,用于 分布式 ID(如雪花算法)
  2. 浮点数

    1. FLOAT(M, D):4 字节,近似值,科学计算(可容忍误差时用)。
    2. DOUBLE(M, D):8 字节,更大范围/精度。
    3. DECIMAL(M, D)精确计算 (如金额、金融数据),M 为总位数,D 为小数位。
  3. 高频DECIMAL(10,2) 表示金额(如 99999999.99)。


二、日期与时间类型

  1. DATE:仅日期,格式 YYYY-MM-DD
  2. TIME:仅时间,格式 HH:MM:SS
  3. DATETIME:日期+时间,格式 YYYY-MM-DD HH:MM:SS无时区高频:记录用户注册时间、订单创建时间。
  4. TIMESTAMP:日期+时间,带时区 (自动转 UTC 存储),范围 1970-2038 年。 高频 :需要自动更新时间戳的字段(如 ON UPDATE CURRENT_TIMESTAMP)。

三、字符串类型

  1. 定长/变长

    1. CHAR(N):固定长度(0 - 255字节),存储定长数据(如 MD5 哈希值)。
    2. VARCHAR(N):可变长度(0 - 65535 字节),高频用于可变长文本(用户名、地址)。
  2. 对比

    1. CHAR 查询更快,但浪费空间(适合短且固定的值)。
    2. VARCHAR 更省空间(适合长度波动大的文本)。
  3. 长文本

    1. TEXT:存储大段文本(如文章内容),最多 65KB。
    2. LONGTEXT:最大 4GB 文本。
  4. 二进制数据

    1. BLOB:存储二进制文件(如图片、音频),但实际开发中通常存文件路径
  5. 枚举与集合

    1. ENUM('A','B','C'):单选值(如性别 ENUM('M','F'))。
    2. SET('A','B','C'):多选值(用逗号分隔,如用户兴趣标签)。 注意:扩展性差,慎用(可用关联表替代)。

四、JSON 类型

  • JSON:存储结构化 JSON 数据(MySQL 5.7+ 支持),高频用于 NoSQL 式灵活存储优势 :支持 JSON 路径查询(如 WHERE data->'$.user.name' = 'John'

日常开发中最常用的类型

场景 推荐类型 原因说明
主键 ID INT UNSIGNED 自增、范围大且高效
金额/精确计算 DECIMAL(10,2) 避免浮点误差
用户名字/地址 VARCHAR(255) 灵活节省空间
大段文本 TEXT 支持长内容
时间戳字段 TIMESTAMP 自动时区转换、更新
状态标记(布尔值) TINYINT(1) 替代 BOOLEAN(兼容性更好)
配置/动态数据 JSON 灵活存储非结构化数据

注意事项 ⚠️

  1. 避免过度分配长度 :如 VARCHAR(255) 可能浪费资源,按实际需求定义

  2. TIMESTAMP 的 2038 年问题 :未来需迁移到 DATETIME

  3. ENUM / SET 慎用:修改选项需 DDL 操作,影响并发性能

  4. 数值类型选择 :优先选最小适用类型(如状态用 TINYINT 而非 INT

常用SQL语句

DDL(数据定义)

创建数据库

sql 复制代码
CREATE DATABASE shop 
  CHARACTER SET utf8mb4 
  COLLATE utf8mb4_unicode_ci;
  • CREATE DATABASE → 创建新数据库
  • CHARACTER SET → 指定字符集(推荐 utf8mb4 支持 emoji)
  • COLLATE → 排序规则(unicode_ci 不区分大小写)

创建表

sql 复制代码
-- 创建用户表,包含主键、自增、默认值和唯一约束
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,    
    username VARCHAR(50) NOT NULL UNIQUE, 
    age TINYINT UNSIGNED DEFAULT 18,      
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
  • PRIMARY KEY → 主键(唯一标识每行数据)
  • AUTO_INCREMENT → 自增(常用于主键)
  • DEFAULT → 默认值(插入数据时未指定则填充)
  • UNIQUE → 唯一约束(如用户名不可重复)

表约束

主键 :PRIMARYKEY

一张表中,我们为了区分每一条记录的唯一性 ,必须有一个字段是永远不会重复 ,并且不会为空的 ,这个字段我们通常会将它设置为主键

主键是表中唯一的索引;

并且必须是NOTNULL的,如果没有设置NOTNULL,那么MySQL也会隐式的设置为NOTNULL;

主键也可以是多列索引,PRIMARYKEY(key_Part,..) ,我们一般称之为联合主键

建议:开发中主键字段应该是和业务无关的,尽量不要使用业务字段来作为主键

唯一:UNIQUE

某些字段在开发中我们希望是唯一 的,不会重复的,比如手机号码、身份证号码等,这个字段我们可以使用UNIQUE来约束:

使用UNIQUE约束的字段在表中必须是不同的

UNIQUE索引I允许NULL包含的列具有多个值NULL

不能为空:NOTNULL

口某些字段我们要求用户必须插入值,不可以为空,这个时候我们可以使用NOTNULL来约束;

默认值:DEFAULT

口某些字段我们希望在没有设置值时给予一个默认值,这个时候我们可以使用DEFAULT来完成;

自动递增:AUTO_INCREMENT

口某些字段我们希望不设置值时可以进行递增,比如用户的id,这个时候可以使用AUTO_INCREMENT来完成;

修改表结构 (ALTER)

添加列
sql 复制代码
-- 向 `users` 表添加手机号字段
ALTER TABLE users 
  ADD COLUMN mobile VARCHAR(15) NOT NULL AFTER username;
  • ADD COLUMN → 添加新列
  • AFTER username → 指定列的位置(可选)
修改列类型
sql 复制代码
-- 将 `email` 字段长度扩展到 150
ALTER TABLE users 
  MODIFY COLUMN email VARCHAR(150) UNIQUE;

修改类型可能导致数据截断(如 VARCHAR(100)VARCHAR(50)

重命名表
sql 复制代码
-- 将表名 `users` 改为 `members`
RENAME TABLE users TO members;
删除列
sql 复制代码
-- 删除 `users` 表的 `age` 字段
ALTER TABLE users 
  DROP COLUMN age;
删除表
sql 复制代码
-- 删除 `temp_data` 表(谨慎操作!)
DROP TABLE IF EXISTS temp_data;
  • IF EXISTS → 避免表不存在时报错

DML (数据操作)

插入数据 INSERT

sql 复制代码
-- 插入一条用户数据,指定字段名和值
INSERT INTO users (name, email, created_at) 
VALUES ('王五', '[email protected]', NOW());
  • INSERT INTO users:向 users 表插入数据
  • (name, email, created_at):指定要插入的字段
  • VALUES (...):对应字段的值
  • NOW():函数,插入当前时间 注意 :字段顺序必须与值一一对应,未指定的字段会用默认值或 NULL

更新数据 UPDATE

sql 复制代码
-- 将 id=5 的用户年龄改为 25,并记录修改时间UPDATE users 
SET age = 25, updated_at = CURRENT_TIMESTAMP 
WHERE id = 5;
  • UPDATE users:更新 users
  • SET age = 25:设置 age 字段为 25
  • updated_at = CURRENT_TIMESTAMP:同时更新修改时间
  • WHERE id = 5关键约束 ,只修改 id=5 的行(无 WHERE 会更新全表!)

删除数据 DELETE

sql 复制代码
-- 删除邮箱为 NULL 且注册时间在 2020 年前的无效用户DELETE FROM users 
WHERE email IS NULL
  AND created_at < '2020-01-01';
  • DELETE FROM users:从 users 表删除数据
  • WHERE ...必须指定条件,否则清空整个表!
  • email IS NULL:判断邮箱为空的记录
  • AND:同时满足两个条件

DQL (数据查询)

基础查询

sql 复制代码
-- 查询年龄大于 18 岁的用户,按注册时间倒序排列,取前 10 条SELECT id, name, age 
FROM users 
WHERE age > 18 
ORDER BY created_at DESC 
LIMIT 10 OFFSET 20;
  • SELECT id, name, age:选择要查询的字段(避免用 * 提升性能)
  • FROM users:从 users 表查询
  • WHERE age > 18:过滤条件
  • ORDER BY created_at DESC:按注册时间倒序 (ASC升序[默认] DESC降序)
  • LIMIT 10:限制返回 10 条 (分页查询)
  • OFFSET 20:数据偏移

多表连接 JOIN

sql 复制代码
-- 查询订单详情:关联用户表和订单表SELECT o.order_id, u.name, o.amount 
FROM orders o 
INNER JOIN users u ON o.user_id = u.id 
WHERE o.status = 'paid';
  • INNER JOIN:内连接,只返回两表匹配的行
  • orders o:给 orders 表起别名 o
  • ON o.user_id = u.id:连接条件(订单的用户ID = 用户的ID)
  • WHERE o.status = 'paid':筛选已支付的订单

聚合与分组 GROUP BY

sql 复制代码
-- 统计每个部门的平均工资,且只显示平均工资大于 10000 的部门SELECT department, AVG(salary) AS avg_salary 
FROM employees 
GROUP BY department 
HAVING avg_salary > 10000;

解释

  • AVG(salary):计算平均工资
  • AS avg_salary:给计算结果起别名
  • GROUP BY department:按部门分组
  • HAVING avg_salary > 10000:对分组后的结果过滤(WHERE 不能用于聚合函数)

聚合函数

什么是聚合函数?

聚合函数对一组值 执行计算并返回单个汇总值 ,常用于统计、分组、汇总数据 核心特点

  • 通常与 GROUP BY 子句配合使用(分组统计)
  • 忽略 NULL 值(除非特别处理)
  • 默认对所有行进行计算,可用 DISTINCT 去重后再聚合

常见聚合函数及用法

函数 作用 示例 高频场景
COUNT() 统计行数或非 NULL 值数量 SELECT COUNT(*) FROM users 统计总记录数、满足条件的数量
SUM() 数值列求和 SELECT SUM(salary) FROM employees 计算总和(如销售额、库存总量)
AVG() 数值列平均值 SELECT AVG(score) FROM exams 计算平均分、平均价格
MAX() 最大值 SELECT MAX(price) FROM products 找最高分、最新日期、最大金额
MIN() 最小值 SELECT MIN(created_at) FROM orders 找最低分、最早日期
GROUP_CONCAT() 将分组结果拼接成字符串 SELECT GROUP_CONCAT(name) FROM ... 合并多行文本(如用户标签列表)
  1. 基础统计(不分组)
sql 复制代码
SELECT
  COUNT(*) AS total_users,AVG(age) AS avg_age,MAX(balance) AS max_balance 
FROM users;

作用: 直接对整个表进行统计,不按任何字段分组

  • COUNT(*):统计表中所有行的总数(包括 NULL 值)
  • AVG(age):计算所有用户的平均年龄(自动跳过 ageNULL 的行)
  • MAX(balance):找出表中最大的余额值

输出示例

total_users avg_age max_balance
1000 28.5 99999.99

适用场景

  • 快速获取全表核心指标(如用户总数、平均值、极值)
  • 适用于数据报表的摘要部分或管理后台的统计面板

  1. 分组统计( GROUP BY
sql 复制代码
SELECT
  department_id,COUNT(*) AS emp_count,AVG(salary) AS avg_salary 
FROM employees 
GROUP BY department_id;

作用 : 按 department_id 将员工分组,统计每个部门的员工数量和平均工资

  • GROUP BY department_id:将数据按部门分组,每个部门单独计算
  • COUNT(*):统计每个部门的员工总数
  • AVG(salary):计算每个部门的平均工资

输出示例

department_id emp_count avg_salary
1 50 8000
2 30 12000

适用场景

  • 按维度分析数据(如按地区统计销售额、按月份统计订单量)
  • 常用于生成分组报表或可视化图表的数据源

  1. 过滤分组结果( HAVING
sql 复制代码
SELECT
  customer_id,
  COUNT(*) AS order_count 
FROM orders 
GROUP BY customer_id 
HAVING order_count > 100;

作用: 按客户分组统计订单数量,并筛选出订单数超过 100 的客户

  • GROUP BY customer_id:按客户分组
  • COUNT(*):统计每个客户的订单总数
  • HAVING order_count > 100:过滤出订单数大于 100 的分组

输出示例

customer_id order_count
101 150
202 200

WHERE 的区别

  • WHERE 在分组前过滤行(如 WHERE order_date > '2023-01-01'
  • HAVING 在分组后过滤分组(必须依赖聚合结果或分组字段)

适用场景

  • 筛选出满足条件的分组(如高价值客户、热门商品)
  • 常用于识别异常数据或重点分析对象

  1. 结合 DISTINCT 去重统计
sql 复制代码
SELECT
  COUNT(DISTINCT ip_address) AS unique_visitors 
FROM access_logs;

作用: 统计访问日志中不同 IP 地址的数量(去重后计数)

  • DISTINCT ip_address:先对 ip_address 去重,再统计数量
  • 如果直接 COUNT(ip_address) 会统计所有 IP(含重复值)

输出示例

unique_visitors
35678

适用场景

  • 统计唯一值数量(如活跃用户数、独立访客数)
  • 避免重复数据干扰统计结果

  1. 字符串聚合( GROUP_CONCAT
sql 复制代码
SELECT
  order_id,
  GROUP_CONCAT(product_name SEPARATOR ', ') AS products 
FROM order_items 
GROUP BY order_id;

作用: 将同一订单的商品名称合并成一个字符串(用逗号分隔)

  • GROUP_CONCAT(product_name):默认用逗号分隔
  • SEPARATOR ', ':可自定义分隔符(如换行符 \n

输出示例

order_id products
1001 手机, 耳机, 充电宝
1002 笔记本电脑, 鼠标

适用场景

  • 将多行数据合并为单行展示(如订单商品列表、用户标签)
  • 简化数据展示格式,便于导出或前端渲染

关键区别与注意事项

  1. WHERE vs HAVING

    1. WHERE:过滤原始数据行,执行在聚合前
    sql 复制代码
    -- 统计 2023 年每个客户的订单数SELECT customer_id, COUNT(*) 
    FROM orders 
    WHERE order_date >= '2023-01-01'  -- 先过滤行GROUP BY customer_id;
    1. HAVING:过滤分组后的结果,依赖聚合值
    sql 复制代码
    -- 筛选出 2023 年订单数超过 50 的客户SELECT customer_id, COUNT(*) 
    FROM orders 
    WHERE order_date >= '2023-01-01'GROUP BY customer_id 
    HAVING COUNT(*) > 50;  -- 再过滤分组
  2. 聚合函数 NULL

    1. 所有聚合函数(如 SUM, AVG)默认忽略 NULL
    2. 若需将 NULL 视为 0 参与计算,需用 COALESCE
    sql 复制代码
    SELECT AVG(COALESCE(salary, 0)) FROM employees;  -- NULL 转为 0
  3. 性能优化

    1. 对大表分组时,尽量先通过 WHERE 缩小数据范围
    2. GROUP BY 的列添加索引(如 INDEX(department_id)

外键

用通俗的方法解释就是,假如你有两个表格,一个是 "班级表",记录着每个班级的信息,比如班级编号、班级名称;另一个是 "学生表",记录着每个学生的信息,像学生编号、学生姓名、所在班级。在 "学生表" 里,为了表明每个学生属于哪个班级,就需要一个字段来引用 "班级表" 里的班级编号,这个用来引用其他表中主键的字段就叫做外键

外键约束

外键约束就像是一种规则,它规定了外键字段的值必须是被引用表(也就是 "班级表")中主键字段已经存在的值,或者可以为 NULL。这就好比学校规定每个学生必须属于一个已经存在的班级,不能说自己属于一个根本不存在的班级。外键约束可以保证数据的一致性和完整性

sql 复制代码
-- 创建班级表
CREATE TABLE classes (
    class_id INT PRIMARY KEY,
    class_name VARCHAR(50)
);

-- 创建学生表,并添加外键约束
CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(50),
    class_id INT,
    -- 添加外键约束,class_id 引用 classes 表的 class_id
    FOREIGN KEY (class_id) REFERENCES classes(class_id)
);

-- 向班级表中插入数据
INSERT INTO classes (class_id, class_name) VALUES (1, '一年级一班');
INSERT INTO classes (class_id, class_name) VALUES (2, '一年级二班');

-- 向学生表中插入数据
-- 这个插入操作会成功,因为 class_id 1 存在于 classes 表中
INSERT INTO students (student_id, student_name, class_id) VALUES (1, '张三', 1);

-- 这个插入操作会失败,因为 class_id 3 不存在于 classes 表中
-- INSERT INTO students (student_id, student_name, class_id) VALUES (2, '李四', 3);

更新

sql 复制代码
-- 创建班级表,添加 ON UPDATE CASCADE 选项
CREATE TABLE classes (
    class_id INT PRIMARY KEY,
    class_name VARCHAR(50)
);

-- 创建学生表,添加外键约束并设置 ON UPDATE CASCADE
CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(50),
    class_id INT,
    FOREIGN KEY (class_id) REFERENCES classes(class_id)
    ON UPDATE CASCADE
);

-- 向班级表中插入数据
INSERT INTO classes (class_id, class_name) VALUES (1, '一年级一班');
INSERT INTO classes (class_id, class_name) VALUES (2, '一年级二班');

-- 向学生表中插入数据
INSERT INTO students (student_id, student_name, class_id) VALUES (1, '张三', 1);
INSERT INTO students (student_id, student_name, class_id) VALUES (2, '李四', 2);

-- 更新班级表中 class_id 为 1 的记录,将 class_id 更新为 3
UPDATE classes
SET class_id = 3
WHERE class_id = 1;

-- 查看更新后的学生表,发现学生表中 class_id 为 1 的记录也自动更新为 3
SELECT * FROM students;
  • 在创建students表时,使用ON UPDATE CASCADE选项,表示当更新classes表的class_id时,students表中引用该class_id的记录会自动更新
  • 执行UPDATE语句更新classes表中class_id为 1 的记录,将其更新为 3。由于设置了ON UPDATE CASCADEstudents表中class_id为 1 的记录也会自动更新为 3

删除

sql 复制代码
-- 创建班级表,添加 ON DELETE CASCADE 选项
CREATE TABLE classes (
    class_id INT PRIMARY KEY,
    class_name VARCHAR(50)
);

-- 创建学生表,添加外键约束并设置 ON DELETE CASCADE
CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(50),
    class_id INT,
    FOREIGN KEY (class_id) REFERENCES classes(class_id)
    ON DELETE CASCADE
);

-- 向班级表中插入数据
INSERT INTO classes (class_id, class_name) VALUES (1, '一年级一班');
INSERT INTO classes (class_id, class_name) VALUES (2, '一年级二班');

-- 向学生表中插入数据
INSERT INTO students (student_id, student_name, class_id) VALUES (1, '张三', 1);
INSERT INTO students (student_id, student_name, class_id) VALUES (2, '李四', 2);

-- 删除班级表中 class_id 为 1 的记录
DELETE FROM classes WHERE class_id = 1;

-- 查看学生表,发现学生表中 class_id 为 1 的记录也自动删除
SELECT * FROM students;
  • 在创建students表时,使用ON DELETE CASCADE选项,表示当删除classes表的记录时,students表中引用该记录的class_id的记录会自动删除。
  • 执行DELETE语句删除classes表中class_id为 1 的记录。由于设置了ON DELETE CASCADEstudents表中class_id为 1 的记录也会自动删除

多表查询

SQL join 用于把来自两个或多个表的行结合起来

下图展示了 LEFT JOIN、RIGHT JOIN、INNER JOIN、OUTER JOIN 相关的 7 种用法

LEFT JOIN (左连接)

左连接会返回左表中的所有行,以及右表中满足连接条件的行。若右表中没有匹配的行,那么右表的列值会显示为 NULL。可以理解为以左表为基础,尽量去匹配右表的数据

sql 复制代码
SELECT orders.order_id, customers.customer_name
FROM orders
LEFT JOIN customers
ON orders.customer_id = customers.customer_id;

RIGHT JOIN (右连接)

右连接和左连接相反,它会返回右表中的所有行,以及左表中满足连接条件的行。若左表中没有匹配的行,左表的列值会显示为 NULL。也就是以右表为基础,去匹配左表的数据

sql 复制代码
SELECT orders.order_id, customers.customer_name
FROM orders
RIGHT JOIN customers
ON orders.customer_id = customers.customer_id;

这个查询会返回 customers 表中的所有客户

即便某些客户没有对应的订单信息(此时 orders.order_id 会显示为 NULL

INNER JOIN (内连接)

这是最简单、最容易理解、最常用的JOIN方式

内连接查询返回表A和表B中所有匹配行的结果

假设存在两个表,orders(订单表)和 customers(客户表)

orders 表有 order_idcustomer_id 等列

customers 表有 customer_idcustomer_name 等列

sql 复制代码
SELECT orders.order_id, customers.customer_name
FROM orders
INNER JOIN customers
ON orders.customer_id = customers.customer_id;

仅当 orders 表中的 customer_idcustomers 表中的 customer_id 相匹配时

对应的行才会被选取出来

OUTER JOIN (外连接)

OUTER JOIN也可以当作是FULL OUTER JOIN(全外连接) 或者FULL JOIN(全连接)

返回左表和右表中的所有行。当某一行在另一个表中没有匹配的行时,对应的列值会显示为 NULL

可以看作是左连接和右连接的并集

我们需要使用UNION来实现

sql 复制代码
-- 选取左连接的结果
SELECT orders.order_id, customers.customer_name
FROM orders
LEFT JOIN customers
ON orders.customer_id = customers.customer_id
UNION
-- 选取右连接的结果
SELECT orders.order_id, customers.customer_name
FROM orders
RIGHT JOIN customers
ON orders.customer_id = customers.customer_id;

此查询会返回 orders 表和 customers 表中的所有行,无论是否有匹配关系

LEFT Excluding JOIN (左排除连接)

左排除连接用于返回左表中那些在右表中没有匹配记录的行。它本质上是在左连接的基础上,排除掉左右表都有匹配的部分,只保留左表中独有的行

假设我们有两个表:orders 表和 customers 表。orders 表记录订单信息,包含 order_idcustomer_id 列;customers 表记录客户信息,包含 customer_idcustomer_name 列。以下是实现左排除连接的 SQL 代码

sql 复制代码
SELECT orders.*
FROM orders
LEFT JOIN customers
ON orders.customer_id = customers.customer_id
WHERE customers.customer_id IS NULL;
  • 首先使用 LEFT JOINorders 表和 customers 表进行左连接,这样会得到 orders 表的所有行以及 customers 表中匹配的行。
  • 然后通过 WHERE customers.customer_id IS NULL 过滤掉那些在 customers 表中有匹配记录的行,最终只保留 orders 表中没有对应客户记录的行。

RIGHT Excluding JOIN(右排除连接)

右排除连接用于返回右表中那些在左表中没有匹配记录的行。它是在右连接的基础上,排除掉左右表都有匹配的部分,只保留右表中独有的行

sql 复制代码
SELECT customers.*
FROM orders
RIGHT JOIN customers
ON orders.customer_id = customers.customer_id
WHERE orders.order_id IS NULL;
  • 先使用 RIGHT JOINorders 表和 customers 表进行右连接,得到 customers 表的所有行以及 orders 表中匹配的行。
  • 接着通过 WHERE orders.order_id IS NULL 过滤掉那些在 orders 表中有匹配记录的行,最终只保留 customers 表中没有对应订单记录的行

OUTER Excluding JOIN(全外排除连接)

OUTER Excluding JOIN 并非标准 SQL 术语,但可理解为在 全外连接(FULL OUTER JOIN) 的基础上,排除掉左右表都匹配的行,仅保留单边存在的记录(即左表独有或右表独有的行)

sql 复制代码
SELECT *
FROM products
FULL OUTER JOIN brand
  ON products.brand_id = brand.id
WHERE products.brand_id IS NULL  -- 右表独有(左表无匹配)
   OR brand.id IS NULL;          -- 左表独有(右表无匹配)

多对多关系表

在数据库设计中,多对多关系是指一个表中的多条记录可以与另一个表中的多条记录相关联。为了处理这种关系,通常需要引入一个中间表(也称为联结表或关联表)

多对多关系表结构原理

假设有两个实体,分别用表 A 和表 B 表示,它们之间存在多对多关系。为了表示这种关系,需要创建一个中间表 C,该表至少包含两个外键,分别引用表 A 和表 B 的主键。通过中间表 C,可以将表 A 和表 B 中的记录关联起来。

表结构设计

  • 学生表(students):存储学生的基本信息。
  • 课程表(courses):存储课程的基本信息。
  • 选课表(student_courses):作为中间表,存储学生和课程的关联信息。
sql 复制代码
-- 创建学生表
CREATE TABLE students (
    student_id INT PRIMARY KEY AUTO_INCREMENT,
    student_name VARCHAR(50) NOT NULL,
    -- 可以添加其他学生相关的列,如年龄、性别等
    age INT,
    gender CHAR(1)
);

-- 创建课程表
CREATE TABLE courses (
    course_id INT PRIMARY KEY AUTO_INCREMENT,
    course_name VARCHAR(100) NOT NULL,
    -- 可以添加其他课程相关的列,如学分、授课教师等
    credits INT,
    teacher VARCHAR(50)
);

-- 创建选课表(中间表)
CREATE TABLE student_courses (
    id INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT,
    course_id INT,
    -- 添加外键约束,关联学生表
    FOREIGN KEY (student_id) REFERENCES students(student_id),
    -- 添加外键约束,关联课程表
    FOREIGN KEY (course_id) REFERENCES courses(course_id)
);    
  1. 学生表(students):

    1. student_id:学生的唯一标识符,作为主键,使用 AUTO_INCREMENT 自动生成。
    2. student_name:学生姓名,不能为空。
    3. agegender:学生的年龄和性别,可根据实际需求添加更多列。
  2. 课程表(courses):

    1. course_id:课程的唯一标识符,作为主键,使用 AUTO_INCREMENT 自动生成。
    2. course_name:课程名称,不能为空。
    3. creditsteacher:课程的学分和授课教师,可根据实际需求添加更多列。
  3. 选课表(student_courses):

    1. id:选课记录的唯一标识符,作为主键,使用 AUTO_INCREMENT 自动生成。
    2. student_id:引用 students 表的 student_id,表示选课的学生。
    3. course_id:引用 courses 表的 course_id,表示所选的课程。
    4. 两个外键约束确保了数据的一致性和完整性,即选课记录中的学生和课程必须存在于相应的表中。

多对多表关系数据查询

sql 复制代码
-- 1. 查询每个学生所选的课程
SELECT 
    s.student_name,
    c.course_name
FROM 
    students s
JOIN 
    student_courses sc ON s.student_id = sc.student_id
JOIN 
    courses c ON sc.course_id = c.course_id
ORDER BY 
    s.student_name;

-- 2. 查询某门课程的所有选课学生
SELECT 
    c.course_name,
    s.student_name
FROM 
    courses c
JOIN 
    student_courses sc ON c.course_id = sc.course_id
JOIN 
    students s ON sc.student_id = s.student_id
WHERE 
    c.course_name = '数学';

-- 3. 查询每个学生所选课程的数量
SELECT 
    s.student_name,
    COUNT(sc.course_id) AS course_count
FROM 
    students s
LEFT JOIN 
    student_courses sc ON s.student_id = sc.student_id
GROUP BY 
    s.student_id
ORDER BY 
    s.student_name;

-- 4. 查询没有选课的学生
SELECT 
    s.student_name
FROM 
    students s
LEFT JOIN 
    student_courses sc ON s.student_id = sc.student_id
WHERE 
    sc.student_id IS NULL;

-- 5. 查询选择了所有课程的学生
SELECT 
    s.student_name
FROM 
    students s
JOIN 
    student_courses sc ON s.student_id = sc.student_id
GROUP BY 
    s.student_id
HAVING 
    COUNT(DISTINCT sc.course_id) = (SELECT COUNT(*) FROM courses);

数据库查询结果转化为对象

我们在操作数据库的时候,一般都是多表进行联合查询,而不是独立的单个表

但是进行联合查询的时候它返回的数据是平铺的数据,这就不符合我们日常需求

所以需要转换成对象或是数组的形式返回给到前端

JSON_OBJECT函数

sql 复制代码
SELECT 
    products.id AS id,
    products.title AS title,
    products.price AS price,
    JSON_OBJECT(
        'id', brans.id,
        'name', brans.name,
        'sort', brans.sort
    ) AS brand_info
FROM 
    products
LEFT JOIN 
    brans 
ON 
    products.brand_id = brans.id 
WHERE 
    products.price > 5000;

返回结果示例

json 复制代码
[
  {
    "id": 1,
    "title": "高端笔记本电脑",
    "price": 12000,
    "brand_info": {
      "id": 101,
      "name": "Dell",
      "sort": 5
    }
  },
  {
    "id": 2,
    "title": "4K 显示器",
    "price": 6000,
    "brand_info": null  -- 若无匹配品牌,LEFT JOIN 会返回 NULL
  }
]

数据库查询数据转化为数组

多对多查询里面的数据转化成数组需要使用 JSON_OBJECTJSON_ARRAYAGG 联合使用

sql 复制代码
SELECT 
    s.id AS student_id,
    s.name AS student_name,
    JSON_ARRAYAGG(
        JSON_OBJECT(
            'course_id', c.id,
            'title', c.title,
            'credit', c.credit
        )
    ) AS enrolled_courses
FROM 
    students s
LEFT JOIN 
    student_course sc ON s.id = sc.student_id
LEFT JOIN 
    courses c ON sc.course_id = c.id
GROUP BY 
    s.id, s.name;  -- 按学生分组,聚合课程数据

查询结果示例

json 复制代码
[
  {
    "student_id": 1,
    "student_name": "张三",
    "enrolled_courses": [
      {
        "course_id": 101,
        "title": "数据库原理",
        "credit": 3
      },
      {
        "course_id": 102,
        "title": "算法设计",
        "credit": 4
      }
    ]
  },
  {
    "student_id": 2,
    "student_name": "李四",
    "enrolled_courses": null  -- 未选修任何课程
  }
]
  1. 多对多关系的 JOIN

    1. 通过 student_course 中间表连接学生和课程。
    2. 使用 LEFT JOIN 确保未选课的学生也能被查询到。
  2. JSON ****聚合函数

    1. JSON_OBJECT:将单条课程记录转换为 JSON 对象。
    2. JSON_ARRAYAGG:将所有课程对象聚合成一个 JSON 数组。
  3. 分组 ( GROUP BY )

    1. 必须按学生字段分组(如 s.id),否则会返回重复的学生记录。
  4. 处理 NULL

    1. 若学生未选课,enrolled_courses 会返回 NULL,可通过 COALESCE 设置默认空数组:
    sql 复制代码
    COALESCE(
        JSON_ARRAYAGG(...),
        JSON_ARRAY()  -- 返回空数组 []) AS enrolled_courses

本文为个人学习记录,内容会根据学习进度不定期补充和更新,难免存在一定的疏漏或错误。若发现任何问题,恳请指正与建议

相关推荐
Asthenia04125 小时前
Spring AOP 和 Aware:在Bean实例化后-调用BeanPostProcessor开始工作!在初始化方法执行之前!
后端
Asthenia04126 小时前
什么是消除直接左递归 - 编译原理解析
后端
Asthenia04126 小时前
什么是自上而下分析 - 编译原理剖析
后端
Asthenia04126 小时前
什么是语法分析 - 编译原理基础
后端
Asthenia04127 小时前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom7 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
Asthenia04128 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9658 小时前
ovs patch port 对比 veth pair
后端
Asthenia04128 小时前
Java受检异常与非受检异常分析
后端