总结 MySQL 中的一些基础知识点。
本文尽量按照MySQL 8.0 版本的规则,但是很多参考书籍和资料还是基于 5.* 版本,可能有疏漏。
SQL基础
SQL 全称是 Structure Query Language(结构化查询语言)。主要分为三个类别:
- DDL(Data Definiton Languages)语句:数据定义语言,这些语句定义的不同的数据段、数据库、表、列、索引等数据库对象的定义。常用的关键字包括
create
、drop
、alter
等。 - DML(Data Manipulation Language)语句:数据操纵语句、用于添加、删除、更新和查询数据库记录,并检查数据完整性,常见的语句关键字包括
insert
、delete
、update
和select
等。 - DCL(Data Control Language)语句:数据控制语句,用于控制不同的数据段直接的许可和访问级别的语句。这些语句定义的数据库、表、字段、用户的访问权限和安全级别。主要语句关键字包括
grant
、revoke
等。
DDL语句
数据库增删改查
数据库创建的格式如下:
1 | CREATE DATABASE db_name |
2 | [[DEFAULT] CHARACTER SET charset_name] |
3 | [[DEFAULT] COLLATE collation_name] |
常用的字符集参数为:
1 | create database test_database default CHARACTER SET utf8mb4 collate utf8mb4_unicode_ci; |
建议使用utf8mb4
代替utf8
,后者在存储比如emoji
表情的时候会出错。collate
关键字代表的是字符排序规则,会影响结果展示的顺序和order by
等排序参数。
删除数据库语句:
1 | drop database db_name; |
要修改字符集参数的话:
1 | ALTER DATABASE db_name |
2 | [[DEFAULT] CHARACTER SET charset_name] |
3 | [[DEFAULT] COLLATE collation_name] |
MySQL似乎是没有提供对数据库重命名的方法。
如果想查看一共有多少个数据库,可以用:
1 | show databases; |
会显示包含系统库在内的所有数据库。
表的增删改查
创建表的语句比较复杂,直接把官网文档上的完整表述贴了过来:
1 | CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name |
2 | (create_definition,...) |
3 | [table_options] |
4 | [partition_options] |
5 | |
6 | CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name |
7 | [(create_definition,...)] |
8 | [table_options] |
9 | [partition_options] |
10 | [IGNORE | REPLACE] |
11 | [AS] query_expression |
12 | |
13 | CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name |
14 | { LIKE old_tbl_name | (LIKE old_tbl_name) } |
15 | |
16 | create_definition: |
17 | col_name column_definition |
18 | | {INDEX|KEY} [index_name] [index_type] (key_part,...) |
19 | [index_option] ... |
20 | | {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (key_part,...) |
21 | [index_option] ... |
22 | | [CONSTRAINT [symbol]] PRIMARY KEY |
23 | [index_type] (key_part,...) |
24 | [index_option] ... |
25 | | [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] |
26 | [index_name] [index_type] (key_part,...) |
27 | [index_option] ... |
28 | | [CONSTRAINT [symbol]] FOREIGN KEY |
29 | [index_name] (col_name,...) |
30 | reference_definition |
31 | | check_constraint_definition |
32 | |
33 | column_definition: |
34 | data_type [NOT NULL | NULL] [DEFAULT {literal | (expr)} ] |
35 | [AUTO_INCREMENT] [UNIQUE [KEY]] [[PRIMARY] KEY] |
36 | [COMMENT 'string'] |
37 | [COLLATE collation_name] |
38 | [COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT}] |
39 | [STORAGE {DISK|MEMORY}] |
40 | [reference_definition] |
41 | [check_constraint_definition] |
42 | | data_type |
43 | [COLLATE collation_name] |
44 | [GENERATED ALWAYS] AS (expr) |
45 | [VIRTUAL | STORED] [NOT NULL | NULL] |
46 | [UNIQUE [KEY]] [[PRIMARY] KEY] |
47 | [COMMENT 'string'] |
48 | [reference_definition] |
49 | [check_constraint_definition] |
50 | |
51 | data_type: |
52 | (see Chapter 11, Data Types) |
53 | |
54 | key_part: {col_name [(length)] | (expr)} [ASC | DESC] |
55 | |
56 | index_type: |
57 | USING {BTREE | HASH} |
58 | |
59 | index_option: |
60 | KEY_BLOCK_SIZE [=] value |
61 | | index_type |
62 | | WITH PARSER parser_name |
63 | | COMMENT 'string' |
64 | | {VISIBLE | INVISIBLE} |
65 | |
66 | check_constraint_definition: |
67 | [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED] |
68 | |
69 | reference_definition: |
70 | REFERENCES tbl_name (key_part,...) |
71 | [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] |
72 | [ON DELETE reference_option] |
73 | [ON UPDATE reference_option] |
74 | |
75 | reference_option: |
76 | RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT |
77 | |
78 | table_options: |
79 | table_option [[,] table_option] ... |
80 | |
81 | table_option: |
82 | AUTO_INCREMENT [=] value |
83 | | AVG_ROW_LENGTH [=] value |
84 | | [DEFAULT] CHARACTER SET [=] charset_name |
85 | | CHECKSUM [=] {0 | 1} |
86 | | [DEFAULT] COLLATE [=] collation_name |
87 | | COMMENT [=] 'string' |
88 | | COMPRESSION [=] {'ZLIB'|'LZ4'|'NONE'} |
89 | | CONNECTION [=] 'connect_string' |
90 | | {DATA|INDEX} DIRECTORY [=] 'absolute path to directory' |
91 | | DELAY_KEY_WRITE [=] {0 | 1} |
92 | | ENCRYPTION [=] {'Y' | 'N'} |
93 | | ENGINE [=] engine_name |
94 | | INSERT_METHOD [=] { NO | FIRST | LAST } |
95 | | KEY_BLOCK_SIZE [=] value |
96 | | MAX_ROWS [=] value |
97 | | MIN_ROWS [=] value |
98 | | PACK_KEYS [=] {0 | 1 | DEFAULT} |
99 | | PASSWORD [=] 'string' |
100 | | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} |
101 | | STATS_AUTO_RECALC [=] {DEFAULT|0|1} |
102 | | STATS_PERSISTENT [=] {DEFAULT|0|1} |
103 | | STATS_SAMPLE_PAGES [=] value |
104 | | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY}] |
105 | | UNION [=] (tbl_name[,tbl_name]...) |
106 | |
107 | partition_options: |
108 | PARTITION BY |
109 | { [LINEAR] HASH(expr) |
110 | | [LINEAR] KEY [ALGORITHM={1|2}] (column_list) |
111 | | RANGE{(expr) | COLUMNS(column_list)} |
112 | | LIST{(expr) | COLUMNS(column_list)} } |
113 | [PARTITIONS num] |
114 | [SUBPARTITION BY |
115 | { [LINEAR] HASH(expr) |
116 | | [LINEAR] KEY [ALGORITHM={1|2}] (column_list) } |
117 | [SUBPARTITIONS num] |
118 | ] |
119 | [(partition_definition [, partition_definition] ...)] |
120 | |
121 | partition_definition: |
122 | PARTITION partition_name |
123 | [VALUES |
124 | {LESS THAN {(expr | value_list) | MAXVALUE} |
125 | | |
126 | IN (value_list)}] |
127 | [[STORAGE] ENGINE [=] engine_name] |
128 | [COMMENT [=] 'string' ] |
129 | [DATA DIRECTORY [=] 'data_dir'] |
130 | [INDEX DIRECTORY [=] 'index_dir'] |
131 | [MAX_ROWS [=] max_number_of_rows] |
132 | [MIN_ROWS [=] min_number_of_rows] |
133 | [TABLESPACE [=] tablespace_name] |
134 | [(subpartition_definition [, subpartition_definition] ...)] |
135 | |
136 | subpartition_definition: |
137 | SUBPARTITION logical_name |
138 | [[STORAGE] ENGINE [=] engine_name] |
139 | [COMMENT [=] 'string' ] |
140 | [DATA DIRECTORY [=] 'data_dir'] |
141 | [INDEX DIRECTORY [=] 'index_dir'] |
142 | [MAX_ROWS [=] max_number_of_rows] |
143 | [MIN_ROWS [=] min_number_of_rows] |
144 | [TABLESPACE [=] tablespace_name] |
145 | |
146 | query_expression: |
147 | SELECT ... (Some valid select or union statement) |
可以看到创建表的时候,主要分为三个部分,包括创建定义、表选项、分区选项等,实际上大多数时候用不到这么多功能。
不手动指定的话,模式使用InnoDB
作为数据库引擎,InnoDB
最大支持40亿张表。
一个简单的创建语句:
1 | create table test_table |
2 | ( |
3 | id int not null auto_increment primary key, |
4 | name varchar(20), |
5 | class varchar(20), |
6 | age int |
7 | )engine=InnoDB character set utf8mb4; |
创建完成后,可以使用desc
关键字查看表结构:
1 | desc tablename; |
desc
命令输出的信息相对比较简单,可以使用如下语句查看详细的建表语句:
1 | show create table tablename; |
修改表结构使用alter table
语句:
修改表类型:
ALTER TABLE table_name MODIFY [COLUMN] column_definition [FIRST|AFTER colname]
增加表字段:
ALTER TABLE table_name ADD [COLUMN] column_definition [FIRST|AFTER col_name]
删除表字段:
ALTER TABLE table_name DROP [COLUMN] col_name
修改字段名:
ALTER TABLE tablename CHANGE [COLUMN] old_col_name column_definition [FIRST|AFTER col_name]
修改字段排列顺序:前面介绍的的字段增加和修改语法(ADD/CNAHGE/MODIFY)中,都有一个可选项 first|after
column_name,这个选项可以用来修改字段在表中的位置,默认 ADD 增加的新字段是加在
表的最后位置,而 CHANGE/MODIFY 默认都不会改变字段的位置。重命名表,老版本的MySQL一般使用:
ALTER TABLE test_table RENAME to test_table02;
MySQL 8.0中支持
rename
关键字:rename table test_table02 to test_table;
DML语句
插入记录
表创建好后,就可以往里插入记录了,插入记录的基本语法如下:
1 | INSERT INTO tablename (field1,field2,......fieldn) VALUES(value1,value2,......valuesn); |
更新记录
对于表里的记录值,可以通过 update 命令进行更改,语法如下:
1 | UPDATE tablename SET field1=value1,field2.=value2,......fieldn=valuen [WHERE CONDITION] |
删除记录
如果记录不再需要,可以用 delete 命令进行删除,语法如下:
1 | DELETE FROM tablename [WHERE CONDITION] |
查询记录
SELECT 的语法很复杂,所有这里只介绍最基本的语法:
1 | SELECT * FROM tablename [WHERE CONDITION] |
太复杂的就不重复了,这些都是基础内容,网上的教程一大把。
DCL语句
DCL 语句主要是 DBA 用来管理系统中的对象权限时所使用,一般的开发人员很少使用。只记录几条经常用的吧。
这里经常遇到的就是MySQL 8.0和之前版本的不一致,往往在网上搜到一个命令,拿过来不能用,MySQL 8.0的变动以下几个方面:
验证插件和密码加密方式的变化
在 MySQL 8.0 中,caching_sha2_password
是默认的身份验证插件而不是之前版本的 mysql_native_password
,默认的密码加密方式是 SHA2
。
用户授权和修改密码
之前版本:
1 | GRANT ALL PRIVILEGES ON *.* TO `mike`@`%` IDENTIFIED BY '000000' WITH GRANT OPTION; |
MySQL 8.0版本中正确的授权语句:
1 | CREATE USER 'mike'@'%' IDENTIFIED BY '000000'; |
2 | GRANT ALL ON *.* TO 'mike'@'%' WITH GRANT OPTION; |
可以看到,创建用户和授权是分开的,不再能一个语句搞定。
修改密码:
1 | ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码'; |
2 | FLUSH PRIVILEGES; |
也可以使用mysqladmin命令:
1 | mysqladmin -u root -p password 新密码 |
还有很多其他内容,作为开发不用完全掌握,可以详见这个博文:MySQL 8.0用户和角色管理
MySQL数据类型
数值类型
MySQL 支持所有标准 SQL 中的数值类型,其中包括严格数值类型(INTEGER、SMALLINT、DECIMAL 和 NUMERIC),以及近似数值数据类型(FLOAT、REAL 和 DOUBLE PRECISION),并在此基础上做了扩展。扩展后增加了TINYINT、MEDIUMINT 和 BIGINT 这 3 种长度不同的整型,并增加了 BIT 类型,用来存放位数据。
整型数据
对于整型数据,MySQL 还支持在类型名称后面的小括号内指定显示宽度,例如 int(5)表示当数值宽度小于 5 位的时候在数字前面填满宽度,如果不显示指定宽度则默认为 int(11),一般配合 zerofill 使用。
设置了宽度限制后,如果插入大于宽度限制的值,不会对插入的数据有任何影响,还是按照类型的实际精度进行保存。
小数
对于小数的表示,MySQL 分为两种方式:浮点数和定点数。浮点数包括 float(单精度)和 double(双精度),而定点数则只有 decimal 一种表示。定点数在 MySQL 内部以字符串形式存放,比浮点数更精确,适合用来表示货币等精度高的数据。
浮点数和定点数都可以用类型名称后加“(M,D)”的方式来进行表示,“(M,D)”表示该值一共显示 M 位数字(整数位 + 小数位),其中 D 位位于小数点后面,M 和 D 又称为精度和标度。
由于float和double分别以 32 位和 64 位存储,所以实际上是可能产生精度丢失的。比如一个 float 类型的列,插入 0.123456789 的时候,实际存储到数据库的只是近似值 0.123457,,当位数更多的时候这个问题更明显,比如分别向 float 和 double 类型的列中插入 987654321.123456789,会得到 987654000 和 987654321.1234568。
所以想要存储高精度、准确的数字,还是需要用 decimal 类型。
日期时间类型
MySQL中的事件类型有:DATE
、DATETIME
、TIMESTAMP
、TIME
、YEAR
这些数据类型的主要区别如下:
- 如果要用来表示年月日,通常用 DATE 来表示。
- 如果要用来表示年月日时分秒,通常用 DATETIME 表示。
- 如果只用来表示时分秒,通常用 TIME 来表示。
- 如果需要经常插入或者更新日期为当前系统时间,则通常使用 TIMESTAMP 来表示。TIMESTAMP 值返回后显示为“YYYY-MM-DD HH:MM:SS”格式的字符串,显示宽度固定为 19 个字符。如果想要获得数字值,应在 TIMESTAMP 列添加+0。
- 如果只是表示年份,可以用 YEAR 来表示,它比 DATE 占用更少的空间。YEAR 有 2 位或4 位格式的年。默认是 4 位格式。在 4 位格式中,允许的值是 1901
2155 和 0000。在2 位格式中,允许的值是 7069,表示从 1970~2069 年。 MySQL 以 YYYY 格式显示 YEAR值。
字符串类型
MySQL 包括了 CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM 和 SET 等多种字符串类型。下面的表详细列出了这些字符类型的比较。
CHAR和VCHAR类型
CHAR 和 VARCHAR 很类似,都用来保存 MySQL 中较短的字符串。二者的主要区别在于存储方式的不同:CHAR 列的长度固定为创建表时声明的长度,长度可以为从 0255 的任何值;而 VARCHAR 列中的值为可变长字符串,长度可以指定为065535( 5.0.3以后)之间的值。在检索的时候,CHAR 列删除了尾部的空格,而 VARCHAR 则保留这些空格。
BINARY 和 VARBINARY 类型
BINARY 和 VARBINARY 类似于 CHAR 和 VARCHAR,不同的是它们包含二进制字符串而不包含非二进制字符串。
ENUM 类型
ENUM 中文名称叫枚举类型,它的值范围需要在创建表时通过枚举方式显式指定,对 1255 个成员的枚举需要 1 个字节存储;对于 25565535 个成员,需要 2 个字节存储。最多允许有 65535 个成员。
SET 类型
Set 和 ENUM 类型非常类似,也是一个字符串对象,里面可以包含 0~64 个成员。根据成员的不同,存储上也有所不同。
- 1~8 成员的集合,占 1 个字节。
- 9~16 成员的集合,占 2 个字节。
- 17~24 成员的集合,占 3 个字节。
- 25~32 成员的集合,占 4 个字节。
- 33~64 成员的集合,占 8 个字节。
其他类型
MySQL 8中还支持多种空间类型,应该是用来存储地理信息之类的结构化信息的,但是没找到太多中文资料,平时也没用过,就不误人子弟了。
另外还有新增的 JSON 类型支持。
创建 JSON 字段
创建 JSON 类型的字段很简单,跟其他数据结构没什么区别:
1 | CREATE TABLE t1 (jdoc JSON); |
插入 JSON
1 | INSERT INTO t1 VALUES('{"key1": "value1", "key2": "value2"}'); |
数据库会对JSON的格式进行校验,如果插入错误的格式会报错。
1 | mysql> INSERT INTO t1 VALUES('[1, 2,'); |
2 | ERROR 3140 (22032) at line 2: Invalid JSON text: |
3 | "Invalid value." at position 6 in value (or column) '[1, 2,'. |
查询 JSON
查询 JSON 中的数据用 column->path 的形式,其中对象类型 path 这样表示 $.path, 而数组类型则是 $[index]
查询 testproject 表 student 字段中 JSON 对象 id 为 1 的记录:
1 | SELECT * FROM testproject WHERE student->'$.id'= 1; |
查询 testproject 表 student 字段中 JSON 对象 id 为 1 或 5 的记录:
1 | SELECT * FROM testproject WHERE student->'$.id' in (1,5); |
2 | SELECT * FROM testproject WHERE student->'$.id' = 1 or student->'$.id' = 5; |
更新数据
MySQL 并不支持 column->path 的形式进行更新操作。如果是整个 JSON 更新的话,和插入时类似的。如果需要更新JSON中的某个值,需要用系统提供的函数:
- json_array_append:在json后面追加
- json_array_insert:在指定下标插入
- json_replace:只替换已经存在的旧值,不存在则忽略
- json_set:替换旧值,并插入不存在的新值
- json_insert:插入新值,但不替换已经存在的旧值
- json_remove:删除元素
这部分详细操作可以参考官方文档The JSON Data Type和博客mysql支持原生json使用说明