SQL基础

关系型数据库管理系统(RDBMS):每一行表示一个数据记录,每一列表示一个字段
非关系型数据库管理系统(Non-RDBMS / NoSQL)

  • DDL 数据定义语言(create,drop,alter,truncate)
  • DML 数据操作语言(insert,update,delete,call)
  • DQL 数据查询语言(select,where)
  • DCL 数据控制语言(grant,revoke)

创建数据库

1
2
3
4
5
6
7
8
# 查看数据库
SHOW databases;

# 创建数据库
CREATE DATABASE `name`;

# 删除数据库
DROP DATABASE `name`;

创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 选中数据库
USE game;

# 建表
CREATE TABLE player (
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 + 自增
name VARCHAR(100) NOT NULL, -- 非空约束
level INT DEFAULT 1, -- 默认值约束
exp INT DEFAULT 0,
gold DECIMAL(10,2) DEFAULT 0.00,
email VARCHAR(100) UNIQUE, -- 唯一约束
CHECK (level >= 0) -- 检查约束 (MySQL 8.0.16+)
);

# 查看表
DESC player;

# 修改表结构
ALTER TABLE player MODIFY COLUMN name VARCHAR(200); # 修改数据类型
ALTER TABLE player RENAME COLUMN name to nick_name; # 修改字段名
ALTER TABLE player ADD COLUMN last_login DATETIME; # 添加字段
ALTER TABLE player DROP COLUMN last_login; # 删除字段

# 删除表
DROP TABLE player;

# 清空表(比 DELETE 更快,不可回滚,自增计数器重置)
TRUNCATE TABLE player;

MySQL数据类型

  • 数值类型
    • INT 标准整数
    • TINYINT 非常小的整数
    • BIGINT 极大的整数
    • DECIMAL 定点数(精确小数):例DECIMAL(10,2)表示长度为10,保留2位小数的十进制数值
    • FLOAT/DOUBLE 浮点数
  • 字符串类型
    • VARCHAR 可变长度字符串
    • CHAR 固定长度字符串
    • TEXT 长文本
    • LONGTEXT 极长文本
  • 日期和时间类型
    • DATETIME 日期+时间
    • DATE 仅日期
    • TIME 仅时间
    • TIMESTAMP 时间戳
  • 二进制/其他
    • BLOB 二进制大对象
    • JSON JSON数据
    • ENUM 枚举(限定取值范围,如 ENUM(‘男’,‘女’))
    • SET 集合(可多选,如 SET(‘A’,‘B’,‘C’))

数据的增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 插入数据(一齐修改所有数据且顺序一致可省略前括号)
INSERT INTO player (id, name, level, exp, gold) VALUES (1, '张三', 1, 1, 1);
#插入多条
INSERT INTO player (id, name) VALUES (2,'李四'),(3,'王五');

# 查看数据
SELECT */'column' FROM player;

# 添加/修改默认值
ALTER TABLE player MODIFY level INT DEFAULT 1;

# 修改数据
UPDATE player SET level = 1 where name = '李四';
UPDATE player SET level = 1 where name = '王五';
# 修改所有
UPDATE player SET exp = 0, gold = 0;

# 删除数据
DELETE FROM player where gold = 0;

# 安全提醒:UPDATE 和 DELETE 必须加 WHERE!
-- 执行前先用 SELECT 确认影响范围
-- SET SQL_SAFE_UPDATES = 1; -- 开启安全模式,禁止无 WHERE 的 UPDATE/DELETE

约束

约束用于保证数据的完整性和一致性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 主键约束 (PRIMARY KEY):唯一标识每条记录,非空且唯一
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL
);

# 唯一约束 (UNIQUE):保证字段值不重复
CREATE TABLE user (
email VARCHAR(100) UNIQUE
);

# 非空约束 (NOT NULL)
CREATE TABLE user (
username VARCHAR(50) NOT NULL
);

# 默认值约束 (DEFAULT)
CREATE TABLE user (
status INT DEFAULT 1
);

# 检查约束 (CHECK):MySQL 8.0.16+ 生效
CREATE TABLE user (
age INT CHECK (age >= 0 AND age <= 150)
);

# 外键约束 (FOREIGN KEY):关联其他表的主键
CREATE TABLE equip (
id INT PRIMARY KEY AUTO_INCREMENT,
player_id INT,
name VARCHAR(100),
FOREIGN KEY (player_id) REFERENCES player(id)
ON DELETE CASCADE -- 玩家删除时,装备自动删除
ON UPDATE CASCADE -- 玩家id变更时,同步更新
);

# 外键级联选项
-- ON DELETE/UPDATE CASCADE 级联删除/更新
-- ON DELETE/UPDATE SET NULL 设为 NULL(要求字段可为空)
-- ON DELETE/UPDATE RESTRICT 拒绝操作(默认)
-- ON DELETE/UPDATE NO ACTION 同 RESTRICT

数据的导入和导出

命令行中:

1
2
3
4
5
# 导出
mysqldump -u root -p game (player) > game.sql

# 导入
mysql -u root -p game < game.sql

常用语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# WHERE子句
# 搭配SELECT,UPDATE,DELETE等
SELECT * FROM player WHERE level > 1 AND (level < 5 OR exp > 1) AND exp < 5;

# null的特判
SELECT * FROM player WHERE email is null;
SELECT * FROM player WHERE email is NOT null;
SELECT * FROM player WHERE email = ''; # 空字符串,并非空值NULL

# IN
SELECT * FROM player WHERE level IN (1,3,5);

# BETWEEN
SELECT * FROM player WHERE level BETWEEN 1 AND 10;

# NOT
SELECT * FROM player WHERE level NOT BETWEEN 1 AND 10;

# LIKE
# %任意个字符,_任意一个字符
SELECT * FROM player WHERE name LIKE '%王%';
SELECT * FROM player WHERE name LIKE '王_';

# REGEXP
# 正则表达式:. 任意一个字符 ^ 开头 $ 结尾 [abc] 其中任意一个字符 [a-z] 范围内任意一个字符 A|B A或B
SELECT * FROM player WHERE name REGEXP '^[王张].$' # 王或张姓二字
SELECT * FROM player WHERE email REGEXP '^zhangsan';
SELECT * FROM player WHERE email REGEXP '^[abc]';
SELECT * FROM player WHERE email REGEXP 'net$';

# ORDER BY
SELECT * FROM player ORDER BY level; # 默认升序
SELECT * FROM player ORDER BY level DESC; # 降序
SELECT * FROM player ORDER BY level DESC, exp ASC; # 先level降序,然后exp升序

# 聚合函数
# AVG() COUNT() MAX() MIN() SUM()...
SELECT COUNT(*) FROM player;
SELECT AVG(level) FROM player;

# GROUP BY
SELECT * FROM player GROUP BY sex;
SELECT level, COUNT(*) FROM player GROUP BY level; # 查看每个等级玩家数量

# HAVING 筛选分组后的数据
SELECT level, count(level) from player group by level having count(level) > 4 order by count(level) DESC; # 查看大于4每个等级玩家数量并按人数降序排序

# LIMIT
SELECT * FROM player LIMIT 10; -- 返回前10条
SELECT * FROM player LIMIT 4, 3; -- 偏移4条,返回3条(第5-7条)
SELECT * FROM player LIMIT 3 OFFSET 4; -- 现代推荐写法,同上

# 复杂些的例子:
SELECT substr(name,1,1),count(substr(name,1,1)) from player
GROUP BY substr(name,1,1)
HAVING count(substr(name,1,1)) >= 5
ORDER BY count(substr(name,1,1)) DESC
LIMIT 4,3;

# DISTINCT 去重复
SELECT DISTINCT sex from player;

# UNION 两个查询结果并集(会去重) UNION ALL 不去重
SELECT * FROM player WHERE level BETWEEN 1 AND 3
UNION (ALL)
SELECT * FROM player WHERE exp BETWEEN 1 AND 3;

# INTERSECT 两个查询结果交集
SELECT * FROM player WHERE level BETWEEN 1 AND 3
INTERSECT
SELECT * FROM player WHERE exp BETWEEN 1 AND 3;

# EXCEPT 两个查询结果差集
SELECT * FROM player WHERE level BETWEEN 1 AND 3
EXCEPT
SELECT * FROM player WHERE exp BETWEEN 1 AND 3;

# 常用函数
# 字符串函数
SELECT CONCAT('等级:', level) FROM player;
SELECT LENGTH(name), UPPER(name), LOWER(name) FROM player;
SELECT SUBSTRING(name, 1, 2) FROM player; -- 截取前两个字符
SELECT REPLACE(name, '张', '李') FROM player;

# 日期函数
SELECT NOW(), CURDATE(), CURTIME();
SELECT YEAR(created_at), MONTH(created_at), DAY(created_at) FROM player;
SELECT DATE_FORMAT(created_at, '%Y-%m-%d') FROM player;

# 数值函数
SELECT ROUND(gold, 1), CEIL(gold), FLOOR(gold) FROM player;
SELECT RAND(); -- 0~1 随机数

# 空值处理
SELECT IFNULL(email, '未填写') FROM player;
SELECT COALESCE(email, phone, '未填写') FROM player; -- 返回第一个非 NULL 值

# 条件判断
SELECT name, CASE WHEN level >= 10 THEN '高手' WHEN level >= 5 THEN '老手' ELSE '新手' END AS grade FROM player;

子查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT * from player where level > (SELECT avg(level) from player)

SELECT level, ROUND((SELECT avg(level) from player)) as average,
level - ROUND((SELECT avg(level) from player)) as diff
from player; # as取别名

# 创建新表(从子表)
CREATE TABLE player_above_5 SELECT * FROM player WHERE level < 5;

# 在新表中插入
INSERT INTO player_above_5 select * from player where level between 6 and 10;

# EXISTS
# 返回 0 或 1
SELECT exists( select * from player where level > 10);

# ALL / ANY / SOME
SELECT * FROM player WHERE level > ALL (SELECT level FROM player WHERE exp < 10); -- 大于子查询结果中的所有值
SELECT * FROM player WHERE level > ANY (SELECT level FROM player WHERE exp < 10); -- 大于子查询结果中的任意值(同 SOME)

表关联

本质:笛卡尔积 + 条件过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#用 JOIN ON 关联

# 内连接 INNER JOIN
select * from player
inner join equip
on player.id = equip.player_id;
# 只显示匹配的字段

# 左连接 LEFT JOIN
select * from player
left join equip
on player.id = equip.player_id;
# 某玩家无装备则显示NULL

# 右连接 RIGHT JOIN
select * from player
right join equip
on player.id = equip.player_id;
# 某装备无主人则显示NULL

# 全外连接 FULL JOIN(MySQL不原生支持,用 UNION 模拟)
SELECT * FROM player
LEFT JOIN equip ON player.id = equip.player_id
UNION
SELECT * FROM player
RIGHT JOIN equip ON player.id = equip.player_id;

# 交叉连接 CROSS JOIN(笛卡尔积)
SELECT * FROM player
CROSS JOIN equip;

# 用 WHERE 关联

select * from player, equip
where player.id = equip.player_id;

# 加别名
select * from player p, equip e
where p.id = e.player_id;

索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# CREATE INDEX
# 唯一索引/全文索引/空间索引
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
ON tbl_name (index_col_name,...)

CREATE INDEX email_index on fast_table(email);

# 查看索引
SHOW INDEX from fast_table;

# 删除索引
DROP INDEX email_index on fast_table;

# 用 alter 创建索引
alter table fast_table ADD INDEX email_index(email);

# 注意事项
-- PRIMARY KEY 和 UNIQUE 约束会自动创建索引
-- 索引提高查询速度,但会降低写入速度并占用存储空间
-- 不要给低基数列(如性别)单独建索引,效果差

视图

视图中的数据是动态的,会随着表中数据的变化而变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建视图
CREATE VIEW top10
AS
select * from player order by level DESC limit 10;

# 查看视图
select * from top10;
update player set level = 10 where id = 64;

# 修改视图
ALTER VIEW top10
AS
select * from player order by level ASC limit 10;

# 删除视图
DROP VIEW top10;

事务

事务保证一组操作要么全部成功,要么全部回滚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 开启事务
START TRANSACTION;
-- 或 BEGIN;

UPDATE player SET gold = gold - 100 WHERE id = 1;
UPDATE player SET gold = gold + 100 WHERE id = 2;

# 提交事务(永久生效)
COMMIT;

# 回滚事务(撤销所有操作)
ROLLBACK;

# 保存点(部分回滚)
SAVEPOINT sp1;
-- ... 一些操作
ROLLBACK TO sp1;

事务的 ACID 特性:

  • Atomicity(原子性):要么全做,要么全不做
  • Consistency(一致性):事务前后数据完整性不被破坏
  • Isolation(隔离性):并发事务互不干扰
  • Durability(持久性):事务提交后永久保存