[数据库] 数据库mysql 一文知识大总结(详细)

405 0
Honkers 2026-5-11 17:45:12 来自手机 | 显示全部楼层 |阅读模式

什么是数据库:

数据库 = 用来存数据的地方 + 一套管理这些数据的系统

可以永久储存数据

常见的数据库软件,比如 MySQL,你可以把它理解为:一个专门“管数据”的软件,感觉就类似于c语言里面的文件,只不过更高级一点

数据库的结构

先在脑子里有个概念,明白怎么操作数据库

这个是类似于分层的,

  1. MySQL(管理系统)
  2.   ↓
  3. 数据库(database)
  4.   ↓
  5. 表(table)
  6.   ↓
  7. 数据(一行一行)
复制代码

就是这种感觉,首先进入mysql,mysql里面有很多数据库,然后可以查看有哪些数据库,然后可以选择其中某个进入,进入数据库可以看这个数据库里面的表,也就是数据,表存着数据;

  1. MySQL 软件(整个程序)
  2.   └── 数据库A(比如:学生管理系统)
  3.   │     ├── 表1:学生信息表
  4.   │     ├── 表2:课程表
  5.   │     └── 表3:选课表
  6.   │
  7.   └── 数据库B(比如:电商系统)
  8.         ├── 表1:用户表
  9.         ├── 表2:商品表
  10.         └── 表3:订单表
复制代码

简单操作一下

进入mysql

开启mysql,一般下载的是默认开机自动打开,主包这边把那个关了,如何开启呢

win+R,写入services.msc

找到MYSQL80,手动打开mysql(这边建议在属性里面把mysql启动类型改成手动)

开启后进入 法1:

菜单搜索mysql,点击进入

输入密码

法2:

直接在终端输入mysql -u root -p 终端也是在菜单搜索就行

输入密码

总结一下最基础的操作代码

登录和退出

  1. -- 登录(在终端输入,不是 MySQL 里面)
  2. mysql -u root -p
  3. -- 退出(在 MySQL 里面输入)
  4. exit;
复制代码

数据库操作

  1. -- 1. 查看当前有哪些数据库
  2. SHOW DATABASES;
  3. -- 2. 创建一个新数据库
  4. CREATE DATABASE mydb;
  5. -- 3. 进入某个数据库(使用它)
  6. USE mydb;
  7. -- 4. 查看当前在哪个数据库
  8. SELECT DATABASE();
  9. -- 5. 删除一个数据库(谨慎操作)
  10. DROP DATABASE mydb;
复制代码

表操作

  1. -- 1. 查看当前数据库里有哪些表
  2. SHOW TABLES;
  3. -- 2. 查看某张表的结构
  4. DESC users;
  5. -- 或者
  6. DESCRIBE users;
  7. -- 3. 查看建表语句(看这张表是怎么建的)
  8. SHOW CREATE TABLE users;
复制代码

日常操作就是这几个步骤:

  1. 登录 → SHOW DATABASES → USE xxx → SHOW TABLES → 干活
复制代码

sql语法基础

  1. SELECT 列名
  2. FROM 表名
  3. WHERE 条件
  4. ORDER BY 列名 ASC/DESC
  5. LIMIT 数量;
复制代码

顺序要记住:SELECT → FROM → WHERE → ORDER BY → LIMIT,顺序乱了会报错

下面系统性学习一下数据库

数据类型

比如:

  • 年龄 → 整数

  • 姓名 → 字符串

  • 生日 → 日期

  • 工资 → 小数

所以创建表时:

  1. CREATE TABLE student(
  2.     name VARCHAR(20),
  3.     age INT
  4. );
复制代码

这里:

  • VARCHAR(20) 表示字符串

  • INT 表示整数


一、数值类型

数值类型就是:

存数字

MySQL 中最常用的是:

类型作用
TINYINT很小的整数
INT普通整数
BIGINT大整数
FLOAT单精度小数
DOUBLE双精度小数
DECIMAL精确小数

二、整数类型

1. TINYINT

占 1 个字节

范围很小

  1. age TINYINT
复制代码

适合:

  • 年龄

  • 性别

  • 状态位

因为范围够用,而且省空间


2. INT

最常用的整数类型

占 4 个字节

  1. id INT
  2. score INT
复制代码

开发里:

大部分整数都直接用 INT


3. BIGINT

超大整数

占 8 个字节

  1. qq BIGINT
  2. phone BIGINT
复制代码

适合:

  • QQ号

  • 雪花算法ID

  • 大数据编号

现在很多项目主键都开始用 BIGINT


三、小数类型

1. FLOAT

单精度浮点数

  1. price FLOAT
复制代码

优点:

  • 节省空间

缺点:

  • 不精确

所以:

金额一般不用 FLOAT。

因为可能出现:

  1. 0.1 + 0.2 ≠ 0.3
复制代码

2. DOUBLE

双精度浮点数

比 FLOAT 更精确

  1. height DOUBLE
复制代码

但依然存在精度问题


3. DECIMAL(重点)

精确小数

开发里:

金额必须用 DECIMAL

语法:

  1. DECIMAL(总位数, 小数位数)
复制代码

例如:

  1. salary DECIMAL(10,2)
复制代码

表示:

  1. 总共 10 位
  2. 其中 2 位小数
复制代码

能存:

  1. 12345678.99
复制代码

不能存:

  1. 123456789.99
复制代码

因为超位数了


四、字符串类型

最常用

类型作用
CHAR定长字符串
VARCHAR变长字符串
TEXT长文本

五、CHAR 和 VARCHAR

这是面试高频


1. CHAR

定长字符串

  1. name CHAR(10)
复制代码

无论存:

  1. a
复制代码

还是:

  1. abcdef
复制代码

都会占满 10 个字符空间。

特点:

  • 查询速度快

  • 浪费空间

适合:

  • 性别

  • 身份证固定长度

  • 手机号固定长度


2. VARCHAR(重点)

变长字符串

  1. name VARCHAR(20)
复制代码

如果存:

  1. abc
复制代码

只占 3 个字符长度。

特点:

  • 节省空间

  • 开发最常用

现在开发里:

基本默认 VARCHAR


3. CHAR 和 VARCHAR 区别

类型CHARVARCHAR
长度固定可变
空间浪费节省
速度稍慢
场景固定长度普通字符串

六、TEXT 类型

存大文本

  1. content TEXT
复制代码

适合:

  • 博客内容

  • 文章

  • 评论

  • 富文本

因为 VARCHAR 长度有限

TEXT 可以存很多字符


七、日期时间类型

类型作用
DATE日期
TIME时间
DATETIME日期时间
TIMESTAMP时间戳

八、DATE

只存日期

格式:

  1. YYYY-MM-DD
复制代码

例如:

  1. birthday DATE
复制代码

存:

  1. 2026-05-08
复制代码

九、TIME

只存时间

格式:

  1. HH:MM:SS
复制代码

例如:

  1. start_time TIME
复制代码

存:

  1. 12:30:45
复制代码

十、DATETIME(重点)

存日期 + 时间

格式:

  1. YYYY-MM-DD HH:MM:SS
复制代码

例如:

  1. create_time DATETIME
复制代码

存:

  1. 2026-05-08 12:30:45
复制代码

开发里非常常见


十一、TIMESTAMP

也是日期时间

但是:

它和时区有关

并且:

  • 占用空间更小

  • 自动更新时间

很多项目会这样:

  1. create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
复制代码

表示:

插入数据时自动记录当前时间。


十二、常见字段设计

用户表

  1. CREATE TABLE user(
  2.     id BIGINT,
  3.     username VARCHAR(30),
  4.     password VARCHAR(100),
  5.     age TINYINT,
  6.     salary DECIMAL(10,2),
  7.     create_time DATETIME
  8. );
复制代码

这里:

字段类型原因
idBIGINT 主键
usernameVARCHAR 字符串
passwordVARCHAR
ageTINYINT 足够
salaryDECIMAL 防止精度问题
create_timeDATETIME

十三、最常用的数据类型

类型用途
INT普通整数
BIGINT主键ID
VARCHAR字符串
DECIMAL金额
DATETIME时间
TEXT大文本

十四、面试高频问题

1. 金额为什么不用 FLOAT?

因为 FLOAT 存在精度丢失。

金额必须精确。

所以:

  1. DECIMAL
复制代码

才是标准方案。


2. VARCHAR(20) 的 20 是什么?

表示:

最多存 20 个字符。

不是字节。


3. CHAR 和 VARCHAR 怎么选?

固定长度:

  1. CHAR
复制代码

普通字符串:

  1. VARCHAR
复制代码

开发里基本默认:

  1. VARCHAR
复制代码

MySQL 数据类型主要分三类:

类型作用
数值类型存数字
字符串类型存文本
日期类型存时间
  1. INT
  2. BIGINT
  3. VARCHAR
  4. DECIMAL
  5. DATETIME
  6. TEXT
复制代码

数据库常用约束

什么是约束

在 MySQL 里,约束(Constraint)用来限制表中数据的有效性和完整性

约束保证了数据的合法性,防止脏数据进入数据库。

常见约束包括:

  • 主键约束(PRIMARY KEY)

  • 唯一约束(UNIQUE)

  • 非空约束(NOT NULL)

  • 默认值(DEFAULT)

  • 外键约束(FOREIGN KEY)

  • 自动递增(AUTO_INCREMENT)

  • 检查约束(CHECK)


一、主键约束 PRIMARY KEY

  • 唯一标识表中的每一条记录

  • 不允许重复,也不能为空

示例:

  1. CREATE TABLE student(
  2.     id INT PRIMARY KEY,
  3.     name VARCHAR(20),
  4.     age TINYINT
  5. );
复制代码

特点:

  • 每张表通常只有一个主键

  • 主键列默认隐含 NOT NULL


二、唯一约束 UNIQUE

  • 保证列的值唯一

  • 可以有多个唯一约束列

示例:

  1. CREATE TABLE user(
  2.     id INT PRIMARY KEY,
  3.     username VARCHAR(30) UNIQUE,
  4.     email VARCHAR(50) UNIQUE
  5. );
复制代码

说明:

  • username、email 都不允许重复

  • 但可以为空(如果不加 NOT NULL)


三、非空约束 NOT NULL

  • 不允许字段为 NULL

  • 确保每条记录都有值

示例:

  1. CREATE TABLE student(
  2.     id INT PRIMARY KEY,
  3.     name VARCHAR(20) NOT NULL,
  4.     age TINYINT
  5. );
复制代码

说明:

  • name 字段不能为空

  • age 可以为空


四、默认值 DEFAULT

  • 给字段设置默认值

  • 插入数据时如果不写该字段,则使用默认值

示例:

  1. CREATE TABLE student(
  2.     id INT PRIMARY KEY,
  3.     name VARCHAR(20) NOT NULL,
  4.     age TINYINT DEFAULT 18
  5. );
复制代码

说明:

  • age 没有指定值时,自动为 18


五、自动递增 AUTO_INCREMENT

  • 常用在主键列

  • 每插入一条记录自动加 1

示例:

  1. CREATE TABLE student(
  2.     id INT PRIMARY KEY AUTO_INCREMENT,
  3.     name VARCHAR(20) NOT NULL
  4. );
复制代码

说明:

  • 插入数据时不写 id,系统会自动生成

  • 通常配合主键一起使用


六、外键约束 FOREIGN KEY

  • 用于保证表之间的数据一致性

  • 一张表的字段引用另一张表的主键

示例:

  1. CREATE TABLE class(
  2.     id INT PRIMARY KEY,
  3.     name VARCHAR(20)
  4. );
  5. CREATE TABLE student(
  6.     id INT PRIMARY KEY,
  7.     name VARCHAR(20),
  8.     class_id INT,
  9.     FOREIGN KEY (class_id) REFERENCES class(id)
  10. );
复制代码

说明:

  • student.class_id 必须存在于 class.id

  • 防止出现没有对应班级的学生


七、检查约束 CHECK

  • 用于限制字段值的范围

  • MySQL 8.0+ 才完全支持

示例:

  1. CREATE TABLE student(
  2.     id INT PRIMARY KEY,
  3.     name VARCHAR(20),
  4.     age TINYINT CHECK(age BETWEEN 0 AND 120)
  5. );
复制代码

说明:

  • age 必须在 0~120 之间

  • 不符合条件的插入会报错


八、约束总结

约束用途常用场景
PRIMARY KEY主键唯一且不为空表唯一标识
UNIQUE保证字段值唯一用户名、邮箱
NOT NULL字段不能为空必填字段
DEFAULT默认值插入数据时未指定字段
AUTO_INCREMENT自动递增主键自增
FOREIGN KEY外键约束表关联
CHECK数据有效性限制数值范围、枚举等

建表常用组合示例

  1. CREATE TABLE user(
  2.     id BIGINT PRIMARY KEY AUTO_INCREMENT,
  3.     username VARCHAR(30) NOT NULL UNIQUE,
  4.     password VARCHAR(100) NOT NULL,
  5.     email VARCHAR(50) UNIQUE,
  6.     age TINYINT DEFAULT 18,
  7.     create_time DATETIME DEFAULT CURRENT_TIMESTAMP
  8. );
复制代码

说明:

  • 主键 id 自动递增

  • username 不允许为空且唯一

  • email 唯一

  • age 默认 18

  • create_time 默认当前时间

DDL(数据定义)

什么是 DDL

DDL,全称 Data Definition Language(数据定义语言)

创建数据库, 创建表, 修改表结构, 删除数据库/表都是DDL


一、数据库操作

1. 查询数据库

查询当前 MySQL 中所有数据库: 复数

  1. SHOW DATABASES;
复制代码

查询当前正在使用的数据库:单数

  1. SELECT DATABASE();
复制代码

2. 创建数据库

语法:

  1. CREATE DATABASE 数据库名;
复制代码

示例:

  1. CREATE DATABASE school;
复制代码

指定字符集: default表示默认

  1. CREATE DATABASE school
  2. DEFAULT CHARSET utf8mb4;
复制代码

现在基本都推荐:

  1. utf8mb4
复制代码

因为它支持: 中文,emoji,特殊字符

如果数据库已存在,防止报错:

  1. CREATE DATABASE IF NOT EXISTS school;
复制代码

完整写法:

  1. CREATE DATABASE IF NOT EXISTS school
  2. DEFAULT CHARSET utf8mb4;
复制代码

3. 删除数据库

语法:

  1. DROP DATABASE 数据库名;
复制代码

示例:

  1. DROP DATABASE school;
复制代码

防止数据库不存在报错:

  1. DROP DATABASE IF EXISTS school;
复制代码

4. 使用数据库

进入某个数据库:

  1. USE school;
复制代码

之后所有表操作都会在这个数据库下进行


二、表操作

1. 查看当前数据库中的表

复数

  1. SHOW TABLES;
复制代码

2. 创建表

语法:单数

  1. CREATE TABLE 表名(
  2.     字段名 数据类型,
  3.     字段名 数据类型
  4. );
复制代码

示例:

  1. CREATE TABLE student(
  2.     id INT,
  3.     name VARCHAR(20),
  4.     age INT
  5. );
复制代码

这里:

  • id → 整数

  • name → 字符串

  • age → 整数


3. 查看表结构

查看表字段:

  1. DESC student;
复制代码

或者:

  1. DESCRIBE student;
复制代码

查看建表 SQL:

  1. SHOW CREATE TABLE student;
复制代码

这个特别重要

很多时候:

看别人怎么建表

直接用这个


三、修改表

1. 添加字段

语法:

  1. ALTER TABLE 表名
  2. ADD 字段名 数据类型;
复制代码

示例:

  1. ALTER TABLE student
  2. ADD gender VARCHAR(10);
复制代码

2. 修改字段类型

语法:

  1. ALTER TABLE 表名
  2. MODIFY 字段名 新数据类型;
复制代码

示例:

  1. ALTER TABLE student
  2. MODIFY name VARCHAR(50);
复制代码

把 name 长度改成 50。


3. 修改字段名

语法:

  1. ALTER TABLE 表名
  2. CHANGE 旧字段名 新字段名 新数据类型;
复制代码

示例:

  1. ALTER TABLE student
  2. CHANGE name username VARCHAR(50);
复制代码

注意:

CHANGE 必须重新写数据类型。


4. 删除字段

语法:

  1. ALTER TABLE 表名
  2. DROP 字段名;
复制代码

示例:

  1. ALTER TABLE student
  2. DROP gender;
复制代码

5. 修改表名

语法:

  1. RENAME TABLE 旧表名 TO 新表名;
复制代码

示例:

  1. RENAME TABLE student TO students;
复制代码

四、删除表

1. 删除表

语法:

  1. DROP TABLE 表名;
复制代码

示例:

  1. DROP TABLE student;
复制代码

2. 防止不存在报错

  1. DROP TABLE IF EXISTS student;
复制代码

五、TRUNCATE(清空表)

语法:

  1. TRUNCATE TABLE 表名;
复制代码

示例:

  1. TRUNCATE TABLE student;
复制代码

作用:

  • 删除表中所有数据

  • 保留表结构

相当于:

  1. DELETE FROM student;
复制代码

但是:

TRUNCATE 更快。


六、DDL 常见面试题

1. DELETE 和 TRUNCATE 区别

DELETE:

  • 属于 DML

  • 一行一行删除

  • 可以回滚

  • 可以加 WHERE

  1. DELETE FROM student WHERE id = 1;
复制代码

TRUNCATE:

  • 属于 DDL

  • 直接清空表

  • 速度快

  • 不能加 WHERE

  • 一般不能回滚

  1. TRUNCATE TABLE student;
复制代码

2. DROP、DELETE、TRUNCATE 区别

DELETE

删除数据:

  1. DELETE FROM student;
复制代码

保留:

  • 表结构


TRUNCATE

清空整个表:

  1. TRUNCATE TABLE student;
复制代码

保留: 表结构

重置: 自增 ID


DROP

直接删表:

  1. DROP TABLE student;
复制代码

删除整个表

全部没了


七、DDL 核心总结

DDL 主要就干四件事:

操作关键字
创建CREATE
修改ALTER
删除DROP
清空TRUNCATE

DML

什么是 DML

DML,全称 Data Manipulation Language(数据操作语言)

用来操作表中的数据,包括增、删、改、查。

一、插入数据 INSERT

1. 插入单条记录

语法:

  1. INSERT INTO 表名 (列1, 列2, 列3)
  2. VALUES (值1, 值2, 值3);
复制代码

示例:

  1. INSERT INTO student (id, name, age)
  2. VALUES (1, '张三', 18);
复制代码

2. 插入多条记录

语法:

  1. INSERT INTO 表名 (列1, 列2)
  2. VALUES 
  3.     (值1, 值2),
  4.     (值3, 值4),
  5.     (值5, 值6);
复制代码

示例:

  1. INSERT INTO student (id, name)
  2. VALUES 
  3.     (2, '李四'),
  4.     (3, '王五');
复制代码

3. 插入全部列

如果顺序和表列一致,可以省略列名:

  1. INSERT INTO student
  2. VALUES (4, '赵六', 20);
复制代码

二、删除数据 DELETE

1. 删除指定条件的记录

语法:

  1. DELETE FROM 表名
  2. WHERE 条件;
复制代码

示例:

  1. DELETE FROM student
  2. WHERE id = 3;
复制代码

说明:

  • 没有 WHERE 会删除整张表的数据

  • DELETE 是 DML,可以回滚


2. 删除全部数据

  1. DELETE FROM student;
复制代码

或者使用 TRUNCATE(属于 DDL,速度更快):

  1. TRUNCATE TABLE student;
复制代码

三、更新数据 UPDATE

1. 更新指定记录

语法:

  1. UPDATE 表名
  2. SET 列1=新值1, 列2=新值2
  3. WHERE 条件;
复制代码

示例:

  1. UPDATE student
  2. SET age = 19
  3. WHERE id = 1;
复制代码

说明:

  • WHERE 很重要,否则会更新整张表


2. 更新全部记录

  1. UPDATE student
  2. SET age = 18;
复制代码

四、查询数据 SELECT

1. 查询全部列

  1. SELECT * FROM student;
复制代码

2. 查询指定列

  1. SELECT name, age FROM student;
复制代码

3. 条件查询

  1. SELECT * FROM student
  2. WHERE age > 18;
复制代码

4. 排序查询

  1. SELECT * FROM student
  2. ORDER BY age DESC;
复制代码

5. 限制查询条数

  1. SELECT * FROM student
  2. LIMIT 5;
复制代码

6. 聚合函数

  1. SELECT COUNT(*) FROM student;
  2. SELECT AVG(age) FROM student;
  3. SELECT MAX(age) FROM student;
  4. SELECT MIN(age) FROM student;
复制代码

7. 分组查询

  1. SELECT age, COUNT(*) 
  2. FROM student
  3. GROUP BY age;
复制代码

五、DML 特点

  • 操作的是表数据

  • 可以回滚(事务支持下)

  • 包括增、删、改、查


常用组合示例

  1. -- 插入
  2. INSERT INTO student (name, age) VALUES ('张三', 18);
  3. -- 更新
  4. UPDATE student SET age = 19 WHERE name='张三';
  5. -- 删除
  6. DELETE FROM student WHERE age < 18;
  7. -- 查询
  8. SELECT * FROM student WHERE age >= 18 ORDER BY age DESC LIMIT 10;
复制代码

掌握这几个操作,日常开发中对数据的增删改查基本够用了。

DQL

这个就到重点了 这个建议直接做牛客SQL篇的基础42题,一边做题一边巩固

关键字作用示例
SELECT选列SELECT name, age
AS起别名SELECT name AS 姓名
DISTINCT去重SELECT DISTINCT university
FROM指定表FROM students
WHERE条件筛选WHERE age > 20
AND / OR / NOT逻辑运算WHERE age > 20 AND gender = '男'
IS NULL / IS NOT NULL判断空值WHERE age IS NOT NULL
LIKE模糊匹配WHERE name LIKE '张%'
REGEXP正则匹配WHERE phone REGEXP '^1[0-9]{10}$'
IN多值匹配WHERE id IN (1, 2, 3)
BETWEEN区间WHERE age BETWEEN 20 AND 30
ORDER BY排序ORDER BY age DESC
LIMIT限制条数LIMIT 10
GROUP BY分组GROUP BY university

执行顺序

  1. SELECT 列名               -- 5. 选哪些列
  2. FROM 表名                 -- 1. 从哪张表
  3. JOIN 表2 ON 条件          -- 2. 联表
  4. WHERE 条件                -- 3. 筛选行
  5. GROUP BY 分组列           -- 4. 分组
  6. HAVING 分组筛选条件        -- 5. 筛选分组
  7. ORDER BY 排序列            -- 6. 排序
  8. LIMIT 条数;               -- 7. 限制条数
复制代码

正则表达式

就是模糊匹配LIKE的高级进阶版,用来判断一个字符串是否符合某种模式

怎么使用?

  1. WHERE 字段 REGEXP '规则'       -- 匹配
  2. WHERE 字段 NOT REGEXP '规则'   -- 不匹配
复制代码

基础语法

1. 单字符匹配

  1. SELECT 'a' REGEXP 'a';   -- true
复制代码

2. 点号(.)

. 表示任意单个字符

  1. SELECT 'abc' REGEXP 'a.c';  -- true
复制代码

3. 字符集合 []

[abc] 表示 a 或 b 或 c

  1. SELECT 'a' REGEXP '[abc]';  -- true
  2. SELECT 'd' REGEXP '[abc]';  -- false
复制代码

4. 范围匹配

  1. [a-z] 小写字母
  2. [A-Z] 大写字母
  3. [0-9] 数字
复制代码
  1. SELECT 'g' REGEXP '[a-z]';  -- true
复制代码

5. 取反

[^abc] 表示不是 a、b、c

  1. SELECT 'd' REGEXP '[^abc]';  -- true
复制代码

定位符

1. ^ 开头

  1. SELECT 'abc' REGEXP '^a';  -- true
复制代码

2. $ 结尾

  1. SELECT 'abc' REGEXP 'c$';  -- true
复制代码

3. 完整匹配

^...$ 表示整个字符串必须完全符合规则

  1. SELECT 'abc' REGEXP '^abc$';   -- true
  2. SELECT 'abcd' REGEXP '^abc$';  -- false
复制代码

重复匹配(核心)

1. *

* 表示前一个字符出现 0 次或多次

  1. SELECT 'aaa' REGEXP 'a*';  -- true
复制代码

2. +

+ 表示前一个字符至少出现 1 次

  1. SELECT 'a' REGEXP 'a+';   -- true
  2. SELECT '' REGEXP 'a+';    -- false
复制代码

3. ?

? 表示前一个字符出现 0 次或 1 次


4. {n}

a{3} 表示连续出现 3 次的 a

  1. SELECT 'aaa' REGEXP 'a{3}';  -- true
复制代码

5. {n,m}

a{2,4} 表示 a 出现 2 到 4 次

  1. SELECT 'aa' REGEXP 'a{2,4}';    -- true
  2. SELECT 'aaa' REGEXP 'a{2,4}';   -- true
  3. SELECT 'aaaa' REGEXP 'a{2,4}';  -- true
  4. SELECT 'a' REGEXP 'a{2,4}';     -- false
复制代码

看个题就明白了:

  1. SELECT
  2.     id,
  3.     name,
  4.     phone_number
  5. FROM
  6.     contacts
  7. WHERE
  8.     phone_number REGEXP '^[1-9][0-9]{9}$'
  9.     OR phone_number REGEXP '^[1-9][0-9]{2}-[0-9]{3}-[0-9]{4}$'
  10. ORDER BY
  11.     id ASC;
复制代码

解释: 格式1:^[1-9][0-9]{9}$

  1. ^          → 从开头开始匹配
  2. [1-9]      → 第一位:1-9(不能是0)
  3. [0-9]{9}   → 后面9位:0-9
  4. $          → 到这里必须结束
复制代码

合起来:刚好10位数字,第一位不是0

格式2:^[1-9][0-9]{2}-[0-9]{3}-[0-9]{4}$

  1. ^              → 开头
  2. [1-9][0-9]{2} → 3位数字,第一位不是0
  3. -              → 一个横杠
  4. [0-9]{3}      → 3位数字
  5. -              → 一个横杠
  6. [0-9]{4}      → 4位数字
  7. $              → 结尾
复制代码

合起来:3位-3位-4位,第一位不是0

MySQL 多表查询

一、先搞清楚一件事:为什么要多表?

数据库里的数据,不会全放在一张表里

举个最简单的例子:

用户表 user

idname
1张三
2李四

订单表 orders

iduser_idprice
11100
21200

现在问题来了:

我要查:“张三买了多少钱”

会发现:

  • user 表里没有 price

  • orders 表里没有 name

所以必须:

把两张表“拼在一起”查

这就是多表查询。


二、最原始的多表查询

先看一个朴素的方法:

  1. SELECT *
  2. FROM user, orders;
复制代码

结果会是:

两张表乱配对

  1. 张三 + 订单1
  2. 张三 + 订单2
  3. 李四 + 订单1
  4. 李四 + 订单2
复制代码

明显有问题


三、加一个条件

  1. SELECT *
  2. FROM user, orders
  3. WHERE user.id = orders.user_id;
复制代码

现在就正常了: 只匹配“对得上的”

  1. 张三 + 订单1
  2. 张三 + 订单2
复制代码

四、进化版写法:JOIN

推荐用 JOIN:

  1. SELECT *
  2. FROM user
  3. JOIN orders
  4. ON user.id = orders.user_id;
复制代码

你可以这样理解:

  • FROM user → 先拿用户表

  • JOIN orders → 把订单表拿过来

  • ON → 规定怎么匹配


五、第一种连接:INNER JOIN(默认)

  1. SELECT *
  2. FROM user
  3. INNER JOIN orders
  4. ON user.id = orders.user_id;
复制代码

解释:

只要“匹配成功”的数据

也就是说:

  • 有订单的用户 → 保留

  • 没订单的用户 → 直接消失


六、第二种连接:LEFT JOIN(重点)

现在加一个用户:

idname
3王五

但他没有订单

用 INNER JOIN:

  1. SELECT *
  2. FROM user
  3. JOIN orders
  4. ON user.id = orders.user_id;
复制代码

王五直接没了 (错误)


用 LEFT JOIN:

  1. SELECT *
  2. FROM user
  3. LEFT JOIN orders
  4. ON user.id = orders.user_id;
复制代码

结果:

  1. 张三 + 订单
  2. 李四 + NULL
  3. 王五 + NULL
复制代码

一句话理解:

LEFT JOIN = 左边的表,全都要!

右边没有就补 NULL


七、总结

核心就这两句:

INNER JOIN

只要匹配上的

LEFT JOIN

左表全要,右表没有就 NULL


八、易错:

错误1:不写 ON

  1. JOIN orders
复制代码

直接炸(不知道怎么匹配)


错误2:字段不加表名

  1. SELECT id
复制代码

多表里 id 会冲突

正确写法:

  1. SELECT user.id
复制代码

或者用别名:

  1. SELECT u.id
复制代码

错误3:LEFT JOIN + WHERE 写错

  1. SELECT *
  2. FROM user u
  3. LEFT JOIN orders o ON u.id = o.user_id
  4. WHERE o.price > 100;
复制代码

这会把 LEFT JOIN 变成 INNER JOIN

因为 NULL 被过滤掉了


九、提问:

如何查没有订单的用户?

  1. SELECT u.*
  2. FROM user u
  3. LEFT JOIN orders o ON u.id = o.user_id
  4. WHERE o.id IS NULL;
复制代码
  • 先 LEFT JOIN

  • 再找 NULL


建立一下“感觉”

写多表查询,脑子里就想:

  1. 1. 我要哪几张表?
  2. 2. 它们怎么连?(ON)
  3. 3. 我要保留谁?(JOIN 还是 LEFT JOIN)
复制代码

字符串函数 SUBSTRING_INDEX

在 MySQL 里面,字符串处理非常常见 ,这时候就经常会用到:

  1. SUBSTRING_INDEX()
复制代码

第一次看到这个函数会有点懵

因为它和:

  1. LEFT()
  2. RIGHT()
  3. SUBSTRING()
复制代码

这种函数不太一样


一、SUBSTRING_INDEX 基本语法

  1. SUBSTRING_INDEX(str, delim, count)
复制代码

参数解释:

参数含义
str原字符串
delim分隔符
count取第几个分隔位置

str如果是字符串加'' 如果是列名不加'' count不需要加''


二、最核心的理解

这个函数本质上就一句话:

按照某个分隔符,把字符串切开,然后取其中一部分

重点在于:

  1. count
复制代码

是正数还是负数

因为:

  • 正数 → 从左往右数

  • 负数 → 从右往左数


三、count 为正数(从左往右)

先看字符串:

  1. 'a,b,c,d'
复制代码

1.count = 1

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', 1);
复制代码

含义:

  1. 从左往右,
  2. 找到第1个逗号,
  3. 取它前面的内容
复制代码

结果:

  1. a
复制代码

2.count = 2

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', 2);
复制代码

含义:

  1. 从左往右,
  2. 找到第2个逗号,
  3. 取它前面的内容
复制代码

结果:

  1. a,b
复制代码

3.count = 3

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', 3);
复制代码

结果:

  1. a,b,c
复制代码

四、count 为负数(从右往左)

负数就是从右边开始数。


1.count = -1

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', -1);
复制代码

含义:

  1. 从右往左,
  2. 找到第1个逗号,
  3. 取它后面的内容
复制代码

结果:

  1. d
复制代码

2.count = -2

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', -2);
复制代码

含义:

  1. 从右往左,
  2. 找到第2个逗号,
  3. 取它后面的内容
复制代码

结果:

  1. c,d
复制代码

3.count = -3

  1. SELECT SUBSTRING_INDEX('a,b,c,d', ',', -3);
复制代码

结果:

  1. b,c,d
复制代码

五、实际开发中的经典用法


1.获取邮箱后缀

比如:

  1. abc@qq.com
复制代码

想获取:

  1. qq.com
复制代码

写法:

  1. SELECT SUBSTRING_INDEX(
  2.     'abc@qq.com',
  3.     '@',
  4.     -1
  5. );
复制代码

结果:

  1. qq.com
复制代码

2.获取文件后缀

比如:

  1. test.jpg
复制代码

获取:

  1. jpg
复制代码

写法:

  1. SELECT SUBSTRING_INDEX(
  2.     'test.jpg',
  3.     '.',
  4.     -1
  5. );
复制代码

3.获取 IP 最后一段

  1. 192.168.1.100
复制代码

获取:

  1. 100
复制代码

写法:

  1. SELECT SUBSTRING_INDEX(
  2.     '192.168.1.100',
  3.     '.',
  4.     -1
  5. );
复制代码

4.获取路径最后一级目录

  1. /home/user/mysql
复制代码

获取:

  1. mysql
复制代码

写法:

  1. SELECT SUBSTRING_INDEX(
  2.     '/home/user/mysql',
  3.     '/',
  4.     -1
  5. );
复制代码

六、嵌套使用

这个函数最强的地方:

可以套娃。

比如:

  1. a,b,c,d
复制代码

想取:

  1. b
复制代码

怎么办?


第一步

先取前两个:

  1. SELECT SUBSTRING_INDEX(
  2.     'a,b,c,d',
  3.     ',',
  4.     2
  5. );
复制代码

结果:

  1. a,b
复制代码

第二步

再从右边取一个:

  1. SELECT SUBSTRING_INDEX(
  2.     SUBSTRING_INDEX(
  3.         'a,b,c,d',
  4.         ',',
  5.         2
  6.     ),
  7.     ',',
  8.     -1
  9. );
复制代码

结果:

  1. b
复制代码

当分隔符不存在

比如:

  1. SELECT SUBSTRING_INDEX(
  2.     'abcd',
  3.     ',',
  4.     1
  5. );
复制代码

因为没有逗号

结果会直接返回:

  1. abcd
复制代码

SUBSTRING_INDEX 本质就一句话:

按照某个分隔符切割字符串


正数

  1. 从左往右取
复制代码

负数

  1. 从右往左取
复制代码

SQL 窗口函数

普通聚合函数:多行变一行
窗口函数:保留每一行,同时额外计算统计结果


普通聚合 vs 窗口函数

GROUP BY

GROUP BY 会把多行压缩成一行,原来的明细数据会消失

  1. SELECT class, AVG(score)
  2. FROM student
  3. GROUP BY class;
复制代码

结果:

classavg(score)
1班95
2班91

只能看到分组后的结果,看不到每个学生


窗口函数 OVER()

窗口函数不会合并数据

原来的每一行都保留,只是在右边新增统计列

  1. SELECT
  2.   name,
  3.   class,
  4.   score,
  5.   AVG(score) OVER(PARTITION BY class) avg_score
  6. FROM student;
复制代码

结果:

nameclassscoreavg_score
张三1班9895
李四1班9295
王五2班9191

基础语法

  1. 函数() OVER(
  2.   PARTITION BY 字段
  3.   ORDER BY 字段
  4.   ROWS/RANGE 范围
  5. )
复制代码

OVER() 里面三部分

PARTITION BY

作用:分区(类似分组)

  1. PARTITION BY class
复制代码

表示:

按班级切分数据,每个班单独计算

不写的话,默认整个表是一组


ORDER BY

作用:组内排序,窗口函数的ORDER BY相当于写了最后那个参数(范围)从第一行 到 当前行,(ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

  1. ORDER BY score DESC
复制代码

表示:

每个班内部按成绩降序排列

排名、累加都会按照这个顺序进行


ROWS

作用:控制计算范围

常见写法:

  1. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
复制代码

意思:

从第一行累计到当前行

不写ROWS的话,默认从第一行到最后一行(前提是没在窗口函数GROUP被写的情况下)


排名类窗口函数

准备数据:

nameclassscore
张三1班98
李四1班98
王五1班90
赵六2班95
孙七2班88

SQL:

  1. SELECT
  2.   name,
  3.   class,
  4.   score,
  5.   ROW_NUMBER() OVER(
  6.     PARTITION BY class
  7.     ORDER BY score DESC
  8.   ) rn1,
  9.   RANK() OVER(
  10.     PARTITION BY class
  11.     ORDER BY score DESC
  12.   ) rn2,
  13.   DENSE_RANK() OVER(
  14.     PARTITION BY class
  15.     ORDER BY score DESC
  16.   ) rn3
  17. FROM student;
复制代码

结果:

nameclassscorern1rn2rn3
张三1班98111
李四1班98211
王五1班90332
赵六2班95111
孙七2班88222

三种排名区别

ROW_NUMBER()

连续编号,不管是否重复

  1. 1 2 3 4
复制代码

即使分数相同,也会强制排不同序号


RANK()

并列排名,会跳号

  1. 1 1 3 4
复制代码

两个第一名,下一名直接第三


DENSE_RANK()

并列排名,但不跳号

  1. 1 1 2 3
复制代码

两个第一名,下一名还是第二


聚合类窗口函数

普通聚合函数都能当窗口函数使用: SUM(),AVG(),MAX(),MIN(),COUNT()


统计班级总分和平均分

  1. SELECT
  2.   name,
  3.   class,
  4.   score,
  5.   SUM(score) OVER(
  6.     PARTITION BY class
  7.   ) class_sum,
  8.   AVG(score) OVER(
  9.     PARTITION BY class
  10.   ) class_avg
  11. FROM student;
复制代码

每个学生都还在,同时带出了班级统计数据,不需要子查询,不需要自连接


累加求和

  1. SELECT
  2.   name,
  3.   score,
  4.   SUM(score) OVER(
  5.     ORDER BY score
  6.     ROWS BETWEEN UNBOUNDED PRECEDING
  7.     AND CURRENT ROW
  8.   ) running_sum
  9. FROM student;
复制代码

作用:

从第一行开始,一直累加到当前行


前后行函数

LAG()

取上一行数据

  1. LAG(字段, n)
复制代码

n 表示往前取几行


LEAD()

取下一行数据。

  1. LEAD(字段, n)
复制代码

n 表示往后取几行。


LAG 示例

  1. SELECT
  2.   dt,
  3.   sales,
  4.   LAG(sales,1) OVER(
  5.     ORDER BY dt
  6.   ) prev_sales
  7. FROM sales_data;
复制代码

结果:

日期当天销售额前一天销售额
1号100null
2号150100
3号180150

GROUP BY

把多行压缩成一行


窗口函数

保留所有原始数据,再额外增加统计列。


  1. PARTITION BY:先分组
  2. ORDER BY:组内排序
  3. OVER():告诉窗口函数怎么算
复制代码
  1. GROUP BY:合并行
  2. OVER():保留行
复制代码
  1. 写几个窗口函数
  2. 结果就多几列
复制代码

MySQL 常见数学函数总结

ABS() 绝对值

返回一个数的绝对值。

  1. SELECT ABS(-10);
复制代码

结果:

  1. 10
复制代码

CEIL() / CEILING() 向上取整

向上取整。

  1. SELECT CEIL(3.1);
复制代码

结果:

  1. 4
复制代码
  1. SELECT CEILING(5.01);
复制代码

结果:

  1. 6
复制代码

FLOOR() 向下取整

向下取整。

  1. SELECT FLOOR(3.9);
复制代码

结果:

  1. 3
复制代码

ROUND() 四舍五入

保留整数

  1. SELECT ROUND(3.6);
复制代码

结果:

  1. 4
复制代码

保留小数

  1. SELECT ROUND(3.1415926,2);
复制代码

结果:

  1. 3.14
复制代码

第二个参数表示保留几位小数。


TRUNCATE() 截断小数

直接截断,不会四舍五入。

  1. SELECT TRUNCATE(3.1415926,2);
复制代码

结果:

  1. 3.14
复制代码
  1. SELECT TRUNCATE(3.199,1);
复制代码

结果:

  1. 3.1
复制代码

MOD() 求余

求余数。

  1. SELECT MOD(10,3);
复制代码

结果:

  1. 1
复制代码

等价于:

  1. SELECT 10 % 3;
复制代码

RAND() 随机数

生成 0~1 之间随机数。

  1. SELECT RAND();
复制代码

结果类似:

  1. 0.726381
复制代码

随机获取数据

  1. SELECT *
  2. FROM student
  3. ORDER BY RAND()
  4. LIMIT 3;
复制代码

随机取 3 条数据。


POW() / POWER() 幂运算

计算次方。

  1. SELECT POW(2,3);
复制代码

结果:

  1. 8
复制代码

表示:

复制代码

SQRT() 开平方

  1. SELECT SQRT(16);
复制代码

结果:

  1. 4
复制代码

SIGN() 判断正负

返回:

  • 1:正数

  • 0:0

  • -1:负数

  1. SELECT SIGN(-20);
复制代码

结果:

  1. -1
复制代码

PI() 圆周率

  1. SELECT PI();
复制代码

结果:

  1. 3.141593
复制代码

GREATEST() 最大值

返回多个值中的最大值。

  1. SELECT GREATEST(10,20,30);
复制代码

结果:

  1. 30
复制代码

LEAST() 最小值

返回多个值中的最小值。

  1. SELECT LEAST(10,20,30);
复制代码

结果:

  1. 10
复制代码

EXP() e 的指数

计算:

  1. e^x
复制代码
  1. SELECT EXP(1);
复制代码

结果:

  1. 2.718282
复制代码

LOG() 对数

以 e 为底

  1. SELECT LOG(10);
复制代码

指定底数

  1. SELECT LOG(2,8);
复制代码

结果:

  1. 3
复制代码

表示:

  1. log₂8
复制代码

SIN() / COS() / TAN()

三角函数

  1. SELECT SIN(PI()/2);
复制代码

结果:

  1. 1
复制代码

判断奇偶数

  1. SELECT MOD(id,2)
  2. FROM student;
复制代码

结果:

  • 0:偶数

  • 1:奇数


随机抽奖

  1. SELECT *
  2. FROM user
  3. ORDER BY RAND()
  4. LIMIT 1;
复制代码

最后总结

函数作用
ABS()绝对值
CEIL()向上取整
FLOOR()向下取整
ROUND()四舍五入
TRUNCATE()截断小数
MOD()求余
RAND()随机数
POW()次方
SQRT()开平方
SIGN()判断正负
PI()圆周率
GREATEST()最大值
LEAST()最小值

牛客SQL快速入门42题

SQL1 查询所有列

答:

  1. SELECT* 
  2. FROM  user_profile
复制代码

select就是选择哪列,*表示全部,from后面加被选择的表

SQL2 查询多列

答:

  1. SELECT device_id, gender, age, university 
  2. FROM user_profile;
复制代码

列名和列名之间用 英文逗号 , 隔开

最后一个列名后面不加逗号 ,最后一个列名直接跟 FROM

SQL3 查询结果去重

答:

  1. SELECT DISTINCT university 
  2. FROM user_profile;
复制代码

表里"北京大学"出现了两次,但题目只要每个学校出现一次,直接查 university 列会有重复,得用 DISTINCT 去掉重复值,distinct在英文就是截然不同的,清晰的意思,在数据库起去重作用,只需要在列名之前加上distinct就行

DISTINCT 也可以对多列组合去重:

  1. SELECT DISTINCT university, province FROM user_profile;
复制代码

意思是:学校和省份的组合不重复,比如"北京大学 + Beijing"和"北京大学 + Shanghai"算两条不同的结果

SQL4 查询结果限制返回行数

答:

  1. SELECT device_id 
  2. FROM user_profile 
  3. LIMIT 2;
复制代码

LIMIT 2 的意思是只取前2条数据

如果写 LIMIT 5,就取前5条

LIMIT 永远放在 SQL 语句的最后面 LIMIT 还可以配合偏移量

  1. -- LIMIT 偏移量, 数量
  2. SELECT device_id FROM user_profile LIMIT 0, 2;  -- 从第1条开始,取2条(同上)
  3. SELECT device_id FROM user_profile LIMIT 2, 2;  -- 从第3条开始,取2条
复制代码

SQL 5 将查询后的列重新命名

  1. SELECT device_id AS user_infos_example 
  2. FROM user_profile 
  3. LIMIT 2;
复制代码

给列起别名,关键字:AS AS 就是给列名起一个别名,查出来的数据,列名会显示成 user_infos_example,而不是原来的 device_id

AS 也可以省略不写,直接 SELECT device_id user_infos_example FROM ...,但建议保留 AS,更清晰,不写AS那两个之间没逗号哈,有逗号表示表中的两个列名

  1. -- 没逗号:给 device_id 起别名叫 user_infos_example
  2. SELECT device_id user_infos_example FROM user_profile;
  3. -- 有逗号:查两个列,一个叫 device_id,一个叫 user_infos_example
  4. SELECT device_id, user_infos_example FROM user_profile;
复制代码

SQL 6 查找后排序

  1. SELECT device_id, age 
  2. FROM user_profile 
  3. ORDER BY age ASC;
复制代码

order在英文中就有顺序的意思

ORDER BY age 意思是按 age 这一列排序,ASC 是升序(从小到大),这也是默认的,不写也一样,DESC 是降序(从大到小)

SQL7 查找后多列排序

  1. SELECT device_id, gpa, age 
  2. FROM user_profile 
  3. ORDER BY gpa ASC, age ASC;
复制代码

ORDER BY gpa ASC, age ASC:先按 gpa 升序排,当 gpa 相同的时候,再按 age 升序排,如果 gpa 不同,age 的排序就不起作用

多列排序规则:

  1. ORDER BY 列1 排序方式, 列2 排序方式
复制代码

优先级:列1 > 列2,写在前面的优先级高,只有列1的值相同,才会进入列2的比较

当然还可以先降序再升序:

  1. -- gpa降序,gpa相同时年龄升序
  2. ORDER BY gpa DESC, age ASC;
复制代码

SQL8 查找后降序排列

  1. SELECT device_id,gpa,age
  2. FROM user_profile
  3. ORDER BY gpa DESC,age DESC;
复制代码

和上一个题类似

SQL9 查找学校是北大的学生信息

  1. SELECT device_id,university
  2. FROM user_profile
  3. WHERE university="北京大学";
复制代码

WHERE相当于c语言中的if,还有一定要记得sql中的判断是一个等号=,相当于c语言的==

SQL10 查找年龄大于24岁的用户信息

  1. SELECT device_id,gender,age,university
  2. FROM user_profile
  3. WHERE age>24;
复制代码

我想想,WHERE需要注意的可能也就是WHERE 必须写在 FROM 后面、ORDER BY 前面

SQL11 查找某个年龄段的用户信息

  1. SELECT device_id,gender,age
  2. FROM user_profile
  3. WHERE age>=20 AND age<=23;
复制代码

这里用到关键字AND,AND就类似于c里面的&&

SQLC 语言意思
AND&&两边条件都满足
OR||满足其中一个就行
NOT!取反

SQL12 查找除复旦大学的用户信息

  1. SELECT device_id,gender,age,university
  2. FROM user_profile
  3. WHERE university != "复旦大学";
复制代码

where条件里面写大学不等于复旦大学就可以了

SQL13 用where过滤空值练习

  1. SELECT device_id,gender,age,university
  2. FROM user_profile
  3. WHERE age IS NOT NULL;
复制代码

这里不能使用!=NULL,必须用IS NOT NULL;

SQL14 高级操作符练习(1)

  1. SELECT device_id,gender,age,university,gpa
  2. FROM user_profile
  3. WHERE gender="male" AND gpa>3.5;
复制代码

SQL15 高级操作符练习(2)

  1. SELECT device_id,gender,age,university,gpa
  2. FROM user_profile
  3. WHERE university="北京大学" OR gpa>3.7;
复制代码

使用OR运算符

SQL16 Where in 和Not in

  1. SELECT device_id,gender,age,university,gpa
  2. FROM user_profile
  3. WHERE university="北京大学" OR university="复旦大学" OR university="山东大学";
复制代码

SQL17 操作符混合运用

  1. SELECT device_id,gender,age,university,gpa
  2. FROM user_profile
  3. WHERE (gpa>3.5 AND university="山东大学")OR(gpa>3.8 AND university="复旦大学");
复制代码

SQL18 查看学校名称中含北京的用户

  1. SELECT device_id,age,university
  2. FROM user_profile
  3. WHERE university LIKE"%北京%";
复制代码

LIKE:模糊匹配

%:通配符,代表任意多个字符(包括 0 个)

'%北京%':包含"北京"的字符串,前后都可以有任意字符

SQL19 查找GPA最高值

  1. SELECT MAX(gpa) AS gpa 
  2. FROM user_profile
  3. WHERE university="复旦大学";
复制代码

聚合函数 MAX() MAX(gpa):取 gpa 这一列的最大值 ,WHERE university = '复旦大学':只筛选复旦的学生,AS gpa:给结果列起别名叫 gpa,和题目要求列名一致

常用聚合函数

函数作用
MAX()最大值
MIN()最小值
AVG()平均值
SUM()求和
COUNT()计数

SQL20 计算男生人数以及平均GPA

  1. SELECT 
  2.     COUNT(*) AS male_num,
  3.     ROUND(AVG(gpa),1) AS avg_gpa
  4. FROM user_profile
  5. WHERE gender="male";
复制代码

注意这个不是从上往下执行的,实际执行顺序是这样的:

  1. 第一步:FROM user_profile      → 先找到这张表
  2. 第二步:WHERE gender = 'male'   → 筛出男用户
  3. 第三步:SELECT COUNT(*), AVG(gpa) → 对筛完的数据做聚合计算
复制代码

COUNT(*)就是计算所有的被WHERE条件选中的,然后AS重新起个名字,ROUND的用法是(左边是原始的数值,右边是四舍五入后保留几位数字) 例如:

  1. -- 保留 1 位小数
  2. SELECT ROUND(3.625, 1);   → 3.6
  3. -- 保留 2 位小数
  4. SELECT ROUND(3.625, 2);   → 3.63
  5. -- 保留 0 位小数(取整)
  6. SELECT ROUND(3.625, 0);   → 4
  7. -- 不写第二个参数,默认取整
  8. SELECT ROUND(3.625);      → 4
复制代码

AVG算平均值

重新走一遍这个过程,先从user_profile这个表里面找到满足条件的,gender='male'的,然后走SELECT那一步,先算人数,再算平均值,都重新起个名字

SQL21 分组计算练习题

  1. SELECT
  2.     gender,
  3.     university,
  4.     COUNT(*) AS user_num,
  5.     ROUND(AVG(active_days_within_30), 1) AS avg_active_day,
  6.     ROUND(AVG(question_cnt), 1) AS avg_question_cnt
  7. FROM
  8.     user_profile
  9. GROUP BY
  10.     gender,
  11.     university
  12. ORDER BY
  13.     gender ASC,
  14.     university ASC;
复制代码

执行顺序是先 GROUP BY,再 ORDER BY 这里GROUP的作用就是分组,先分组,看看按照什么分,分组之后SELECT后面的聚合函数计算的都是分组之后的数据

SQL22 分组过滤练习题

  1. SELECT 
  2.     university,
  3.     ROUND(AVG(question_cnt),3) AS avg_question_cnt,
  4.     ROUND(AVG(answer_cnt),3) AS avg_answer_cnt
  5. FROM user_profile
  6. GROUP BY university
  7. HAVING AVG(question_cnt)<5 OR AVG(answer_cnt)<20;
复制代码

对分组后的结果进行筛选,关键字:HAVING

这个是分组后进行筛选,WHRER是分组前筛选

大概步骤如下:① FROM:拿到 7 行 ② GROUP BY university:按学校分成 4 组 ③ SELECT:每组算 AVG(question_cnt)、AVG(answer_cnt) ④ HAVING:筛出平均发帖<5 或 平均回帖<20 的组

SQL23 分组排序练习题

  1. SELECT 
  2.     university,
  3.     ROUND(AVG(question_cnt),4) AS avg_question_cnt
  4. FROM user_profile
  5. GROUP BY university
  6. ORDER BY avg_question_cnt ASC;
复制代码

执行顺序:分组计算排序

  1. FROM → GROUP BY → SELECT → ORDER BY
  2.                          ↑
  3.                    别名在这生效,所以 ORDER BY 能用
复制代码

SQL24 浙江大学用户题目回答情况

  1. SELECT
  2.     q.device_id,
  3.     q.question_id,
  4.     q.result
  5. FROM
  6.     question_practice_detail q
  7.     JOIN user_profile u ON q.device_id = u.device_id
  8. WHERE
  9.     u.university = '浙江大学'
  10. ORDER BY
  11.     q.question_id ASC;
复制代码

先将两个要链接的表起别名简写通过device_id联系在一起,再筛选浙江大学的,最后选出列排序即可

SQL25 统计每个学校的答过题的用户的平均答题数

  1. SELECT 
  2.     u.university,
  3.     ROUND(COUNT(q.question_id)/COUNT(DISTINCT q.device_id),4) AS avg_answer_cnt
  4. FROM user_profile u 
  5. JOIN question_practice_detail q ON u.device_id=q.device_id
  6. GROUP BY u.university
  7. ORDER BY  u.university ASC;
复制代码

先将两个表建立联系,按学校分组,分组后用问题数除以人数即可

SQL26 统计每个学校各难度的用户平均刷题数

  1. SELECT 
  2.     u.university,
  3.     qd.difficult_level,
  4.     ROUND(COUNT(qp.question_id)/COUNT(DISTINCT qp.device_id),4) AS avg_answer_cnt
  5. FROM user_profile u
  6. JOIN question_practice_detail qp ON u.device_id=qp.device_id
  7. JOIN question_detail qd ON qp.question_id=qd.question_id
  8. GROUP BY u.university,qd.difficult_level
复制代码

三个表连接,非常简单,和两个表是一样的道理,继续JOIN ON就行了

SQL27 统计每个用户的平均刷题数

  1. SELECT 
  2.     u.university,
  3.     qd.difficult_level,
  4.     ROUND(COUNT(qp.question_id)/COUNT(DISTINCT qp.device_id),4) AS avg_answer_cnt
  5. FROM  user_profile u
  6. JOIN question_practice_detail qp  ON u.device_id=qp.device_id
  7. JOIN question_detail qd ON qd.question_id=qp.question_id
  8. WHERE u.university="山东大学"
  9. GROUP BY qd.difficult_level;
复制代码

这个和上一题几乎一样,只不过多了个WHERE条件筛选

SQL28 查找山东大学或者性别为男生的信息

  1. -- 先查山东大学
  2. SELECT device_id, gender, age, gpa
  3. FROM user_profile
  4. WHERE university = '山东大学'
  5. UNION ALL
  6. -- 再查男性
  7. SELECT device_id, gender, age, gpa
  8. FROM user_profile
  9. WHERE gender = 'male';
复制代码

题目的意思是把所有山东大学的输出一遍,再把所有男性的输出一遍,两部分拼在一起,中间不删重复的 这个时候就用到UNION ALL

关键字效果
UNION ALL拼接结果,不去重,全部保留
UNION拼接结果,去重,重复的只留一次

SQL29 计算25岁以上和以下的用户数量

  1. SELECT
  2.     CASE
  3.         WHEN age < 25
  4.         OR age IS NULL THEN '25岁以下'
  5.         ELSE '25岁及以上'
  6.     END AS age_cut,
  7.     COUNT(*) AS number
  8. FROM
  9.     user_profile
  10. GROUP BY
  11.     age_cut;
复制代码

这个用到CASE WHEN语法 :

  1. CASE 
  2.     WHEN 条件1 THEN '结果1'
  3.     WHEN 条件2 THEN '结果2'
  4.     ELSE '默认结果'
  5. END
复制代码

然后这里还有疑问的就是,大家都知道GROUP BY是在SELECT 前面执行的,那么为什么GROUP BY还可以使用SELECT 里面起的别名age_cut,MySQL 特殊处理:MySQL 的 GROUP BY 对别名做了特殊兼容,允许使用 SELECT 里的别名;这是 MySQL 独有的宽松语法,但在其他数据库(如 Oracle,SQL Server,PostgreSQL)里,GROUP BY age_cut 会报错

SQL30 查看不同年龄段的用户明细

  1. SELECT 
  2.     device_id,
  3.     gender,
  4. CASE 
  5.     WHEN age<20 THEN"20岁以下"
  6.     WHEN age IS NULL THEN"其他"
  7.     WHEN age >=20 AND age <=24 THEN"20-24岁"
  8.     WHEN age >24 THEN"25岁及以上"
  9. END AS age_cut
  10. FROM user_profile;
复制代码

和上以题逻辑差不多,都是case when

SQL31 计算用户8月每天的练题数量

  1. SELECT 
  2.     DAY(date) AS day,
  3.     COUNT(question_id) AS question_cnt
  4. FROM  question_practice_detail
  5. WHERE MONTH(date)=8 AND YEAR(date)=2021
  6. GROUP BY date;
复制代码

SQL32 计算用户的平均次日留存率

  1. SELECT 
  2.     ROUND(
  3.         SUM(CASE WHEN p2.device_id IS NOT NULL THEN 1 ELSE 0 END) / COUNT(*) , 
  4.         4
  5.     ) AS avg_ret
  6. FROM 
  7.     (SELECT DISTINCT device_id, date FROM question_practice_detail) p1
  8. LEFT JOIN 
  9.     (SELECT DISTINCT device_id, date FROM question_practice_detail) p2
  10. ON 
  11.     p1.device_id = p2.device_id 
  12.     AND p2.date = DATE_ADD(p1.date, INTERVAL 1 DAY);
复制代码

SQL33 统计每种性别的人数

  1. SELECT 
  2.     SUBSTRING_INDEX(profile, ',', -1) AS gender,
  3.     COUNT(*) AS number
  4. FROM user_submit
  5. GROUP BY gender;
复制代码

这里用到:SUBSTRING_INDEX

正数:从左往右数

  1. SUBSTRING_INDEX('a,b,c,d', ',', 1)
复制代码

从左往右,找到第 1 个逗号,取它前面的内容。

结果:a

  1. SUBSTRING_INDEX('a,b,c,d', ',', 2)
复制代码

从左往右,找到第 2 个逗号,取它前面的内容。

结果:a,b

  1. SUBSTRING_INDEX('a,b,c,d', ',', 3)
复制代码

结果:a,b,c

负数:从右往左数

  1. SUBSTRING_INDEX('a,b,c,d', ',', -1)
复制代码

从右往左,找到第 1 个逗号,取它后面的内容。

结果:d

  1. SUBSTRING_INDEX('a,b,c,d', ',', -2)
复制代码

从右往左,找到第 2 个逗号,取它后面的内容。

结果:c,d

  1. SUBSTRING_INDEX('a,b,c,d', ',', -3)
复制代码

结果:b,c,d

SQL34 提取博客URL中的用户名

和上一个题很类似

SQL35 截取出年龄

  1. SELECT 
  2.     SUBSTRING_INDEX(SUBSTRING_INDEX(profile, ',', -2), ',', 1) AS age,
  3.     COUNT(*) AS number
  4. FROM user_submit
  5. GROUP BY age
  6. ORDER BY age DESC;
复制代码

嵌套 SUBSTRING_INDEX 两步取:

第一步:取最后两段

  1. SUBSTRING_INDEX(profile, ',', -2)
复制代码

结果:27,male

第二步:取这段的第一段

  1. SUBSTRING_INDEX(上面的结果, ',', 1)
复制代码

得:27

SQL36 找出每个学校GPA最低的同学

  1. SELECT u.device_id, u.university, u.gpa
  2. FROM user_profile u
  3. JOIN (
  4.     SELECT university, MIN(gpa) AS min_gpa
  5.     FROM user_profile
  6.     GROUP BY university
  7. ) t ON u.university = t.university AND u.gpa = t.min_gpa
  8. ORDER BY u.university ASC;
复制代码

这个是先在内部建立一张表,储存学校和最低的gpa,用"学校相同 且 GPA 等于最低"做关联; 窗口函数,就是在保留原来数据的基础之上可以多建一列数据

或者使用窗口函数:

  1. WITH ranked_gpa AS (
  2.     SELECT 
  3.         device_id,
  4.         university,
  5.         gpa,
  6.         ROW_NUMBER() OVER (PARTITION BY university ORDER BY gpa ASC) AS rn
  7.     FROM user_profile
  8. )
  9. SELECT 
  10.     device_id,
  11.     university,
  12.     gpa
  13. FROM ranked_gpa
  14. WHERE rn = 1
  15. ORDER BY university ASC;
复制代码

WITH ranked_gpa AS (...):定义一个临时表,名叫 ranked_gpa,括号里的 SELECT 就是这个临时表的内容,这个表只在当前 SQL 里有效,执行完就没了

临时表ranked_gpa:

device_iduniversitygparn
6543北京大学3.21
2138北京大学3.42
4321复旦大学3.61
3214复旦大学4.02
2131山东大学3.31
5432山东大学3.82
2315浙江大学3.61

从这个表里面截取rn=1的就好了

SQL37 统计复旦用户8月练题情况

  1. SELECT
  2.     u.device_id,
  3.     u.university,
  4.     COUNT(q.question_id) AS question_cnt,
  5.     SUM(
  6.         CASE
  7.             WHEN q.result = 'right' THEN 1
  8.             ELSE 0
  9.         END
  10.     ) AS right_question_cnt
  11. FROM
  12.     user_profile u
  13.     LEFT JOIN question_practice_detail q ON u.device_id = q.device_id
  14.     AND MONTH(q.date) = 8
  15. WHERE
  16.     u.university = '复旦大学'
  17. GROUP BY
  18.     u.device_id,
  19.     u.university
  20. ORDER BY
  21.     u.device_id;
复制代码

很综合的一道题,题目要求没有回答问题的要记录0,所以不能使用inner join,否则就把没有答题的给删了,此时使用左连接,保留前面那个表的用户,右边如果没有答题就填空,然后计算正确题目数量,sum函数和case when语句结合,then 后面的是返回值,答案正确也就是result=right就返回1,否则返回0,就是啥也不加

SQL38 浙大不同难度题目的正确率

  1. SELECT
  2.     d.difficult_level,
  3.     SUM(
  4.         CASE
  5.             WHEN q.result = 'right' THEN 1
  6.             ELSE 0
  7.         END
  8.     ) / COUNT(*) AS correct_rate
  9. FROM
  10.     user_profile u
  11.     JOIN question_practice_detail q ON u.device_id = q.device_id
  12.     JOIN question_detail d ON q.question_id = d.question_id
  13. WHERE
  14.     u.university = '浙江大学'
  15. GROUP BY
  16.     d.difficult_level
  17. ORDER BY
  18.     correct_rate ASC;
复制代码

个是先把三个表连接,选出浙江大学的,按题目难度分组,最后正确率是对的题目除以总题目

SQL39 21年8月份练题总数

  1. SELECT 
  2.     COUNT(DISTINCT device_id) AS did_cnt,
  3.     COUNT(*)AS question_cnt
  4. FROM  question_practice_detail
  5. WHERE MONTH(date)=8 AND YEAR(date)=2021;
复制代码

这个是先选出2021年8月的,然后直接去重算用户,计算总题数

SQL40 电话号码格式校验

  1. SELECT
  2.     id,
  3.     name,
  4.     phone_number
  5. FROM
  6.     contacts
  7. WHERE
  8.     phone_number REGEXP '^[1-9][0-9]{9}$'
  9.     OR phone_number REGEXP '^[1-9][0-9]{2}-[0-9]{3}-[0-9]{4}$'
  10. ORDER BY
  11.     id ASC;
复制代码

解释: 格式1:^[1-9][0-9]{9}$

  1. ^          → 从开头开始匹配
  2. [1-9]      → 第一位:1-9(不能是0)
  3. [0-9]{9}   → 后面9位:0-9
  4. $          → 到这里必须结束
复制代码

合起来:刚好10位数字,第一位不是0

格式2:^[1-9][0-9]{2}-[0-9]{3}-[0-9]{4}$

  1. ^              → 开头
  2. [1-9][0-9]{2} → 3位数字,第一位不是0
  3. -              → 一个横杠
  4. [0-9]{3}      → 3位数字
  5. -              → 一个横杠
  6. [0-9]{4}      → 4位数字
  7. $              → 结尾
复制代码

合起来:3位-3位-4位,第一位不是0

SQL41 计算每日累计利润

  1. SELECT 
  2.     profit_id,
  3.     profit_date,
  4.     profit,
  5.     SUM(profit) OVER (ORDER BY profit_date ASC) AS cumulative_profit
  6. FROM daily_profits
  7. ORDER BY profit_date ASC;
复制代码

窗口函数只写个GROUP BY不写ROWS是默认从第一行当当前行的计算

SQL42 基本数学函数

  1. SELECT 
  2.     id,
  3.     value,
  4.     ABS(value) AS absolute_value,
  5.     CEIL(value) AS ceiling_value,
  6.     FLOOR(value) AS floor_value,
  7.     ROUND(value, 1) AS rounded_value
  8. FROM numbers
  9. ORDER BY id ASC;
复制代码

都是常见数学函数

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行