0%

MySQL基础

前言

MySQL是我们常用的关系型数据库,今天我们重新回顾一下这个老朋友。

基础概念

数据库(Database):数据库是按照数据结构来组织、存储和管理数据的仓库。

表(Table):表是数据库中用于存储数据的结构,由行(记录)和列(字段)组成。

字段(Field):字段是表中数据的属性,定义了数据的类型和特征。

记录(Record):记录是表中的一行数据,包含所有字段的信息。

主键(Primary Key):主键是表中的一个字段或字段组合,其值在表中唯一,用于唯一标识一条记录。

索引(Index):索引是对表中数据进行排序和检索的一种数据结构,可以加快查询速度。

image-20250113234100787

概念间的关系

SQL类型

  • DDL(Data Definition Language,数据定义语言)用于定义和修改数据库结构,如创建、删除、修改表等;
  • DML(Data Manipulation Language,数据操纵语言)用于对数据库中的数据进行操作,如插入、更新、删除数据等;
  • DCL(Data Control Language,数据控制语言)用于控制用户对数据库的访问权限,如授权、撤销权限等。

数据类型

数值类型

  • 整数类型

    • TINYINT:占用1个字节,有符号范围为-128到127,无符号范围为0到255。
    • SMALLINT:占用2个字节,有符号范围为-32768到32767,无符号范围为0到65535。
    • MEDIUMINT:占用3个字节,有符号范围为-8388608到8388607,无符号范围为0到16777215。
    • INTINTEGER:占用4个字节,有符号范围为-2147483648到2147483647,无符号范围为0到4294967295。
    • BIGINT:占用8个字节,有符号范围为-9223372036854775808到9223372036854775807,无符号范围为0到18446744073709551615。
  • 浮点数类型

    • FLOAT:单精度浮点数,占用4个字节。
    • DOUBLEDOUBLE PRECISION:双精度浮点数,占用8个字节。
    • DECIMALNUMERIC:用于存储精确的小数,需要指定精度和小数位数。
  • 位类型

    • BIT(M):用于存储位字段值,M表示位值的长度。

日期和时间类型

  • DATE:格式为YYYY-MM-DD,仅存储日期信息。
  • TIME:格式为HH:MM:SS,仅存储时间信息,可以包含 fractional seconds precision(小数秒精度)。
  • DATETIME:格式为YYYY-MM-DD HH:MM:SS,同时存储日期和时间信息,可以包含 fractional seconds precision。
  • TIMESTAMP:类似于DATETIME,但范围有限,适合存储时间戳,可以包含 fractional seconds precision。
  • YEAR:用于存储年份信息,格式为YYYY

字符串类型

  • 字符类型

    • CHAR(M):固定长度字符串,M表示最大长度,存储效率高,适用于存储固定长度的数据,如身份证号、MD5值等。
    • VARCHAR(M):可变长度字符串,M表示最大长度,节省存储空间,适用于存储长度不固定的数据,如用户名、地址等。
  • 文本类型

    • TINYTEXT:用于存储较小的文本数据。
    • TEXT:用于存储较大的文本数据,如博客内容、评论等。
    • MEDIUMTEXT:用于存储非常大的文本数据。
    • LONGTEXT:用于存储极大的文本数据。
  • 枚举类型

    • ENUM:用于存储有限值的集合,如性别、状态等固定选项。
  • 集合类型

    • SET:支持存储多个选项的组合,如用户权限、兴趣标签等。

二进制类型

  • 二进制字符串类型

    • BINARY:固定长度的二进制字符串。
    • VARBINARY:可变长度的二进制字符串。
  • 二进制大对象类型

    • TINYBLOB:用于存储较小的二进制数据。
    • BLOB:用于存储较大的二进制数据,如图片、音频、视频等。
    • MEDIUMBLOB:用于存储非常大的二进制数据。
    • LONGBLOB:用于存储极大的二进制数据。

空间数据类型

  • MySQL支持空间数据类型,用于存储和操作地理空间数据,如点、线、多边形等。

JSON数据类型

  • JSON:用于存储JSON格式的数据,方便对JSON数据进行查询和操作。

选择数据类型的依据

  • 存储效率:合理分配字段的存储大小,避免浪费。例如,根据数据的实际范围选择合适的整数类型。
  • 数据准确性:确保字段只能存储符合业务逻辑的数据。例如,年龄字段应选择整数类型,且范围应符合实际年龄的可能值。
  • 查询性能:优化查询速度,减少不必要的计算和转换。例如,对于经常需要进行范围查询的数值字段,选择合适的数据类型和索引可以提高查询效率。
  • 业务需求:根据具体的应用场景和业务需求选择合适的数据类型。例如,金融计算需要高精度,应选择DECIMAL类型;而科学计算对精度要求不高,可以选择FLOATDOUBLE类型。对于日期和时间数据,根据需要存储的信息精度选择DATETIMEDATETIMETIMESTAMP类型。

DDL语句

MySQL的DDL语句(Data Definition Language,数据定义语言)用于定义和修改数据库结构,包括创建、修改、删除数据库和表等对象。

数据库操作

  • 查询所有数据库
1
show databases;
  • 查询当前数据库
1
select database();
  • 创建数据库
1
2
3
create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则];
# 示例
CREATE DATABASE IF NOT EXISTS test_db;
  • 删除数据库
1
2
3
drop database [if exists] 数据库名;
# 示例
DROP DATABASE IF EXISTS test_db;
  • 使用数据库
1
USE 数据库名;

表操作

  • 查询当前数据库所有的表
1
show tables;
  • 查看指定表结构
1
desc 表名
  • 查询指定表的建表语句
1
show create table 表名;
  • 删除表
1
2
3
DROP TABLE [IF EXISTS] 表名;
# 示例
DROP TABLE IF EXISTS users;
  • 删除数据,但保留表结构
1
TRUNCATE TABLE 表名;
  • 创建表结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE 表名(
字段1 字段1类型 [默认值 非空约束] [ COMMENT 字段1注释 ],
字段2 字段2类型 [默认值 非空约束] [COMMENT 字段2注释 ],
字段3 字段3类型 [默认值 非空约束] [COMMENT 字段3注释 ],
......
字段n 字段n类型 [默认值 非空约束] [COMMENT 字段n注释 ]
) [ COMMENT 表注释 ];

# 示例
CREATE TABLE stu02(
ind int not null COMMENT '序号',
name VARCHAR(50) default null COMMENT '姓名',
age int COMMENT '年龄',
gender VARCHAR(1) COMMENT '性别'
) COMMENT '学生表';
  • 表修改

    • 添加字段
    1
    2
    3
    ALTER TABLE 表名 ADD 字段名 数据类型 [默认值 非空约束] [COMMENT 注释];
    # 示例
    ALTER TABLE users ADD email VARCHAR(100);
    • 修改某字段的数据类型
    1
    2
    3
    ALTER TABLE 表名 MODIFY 字段名 新数据类型 [默认值 非空约束]  [COMMENT 注释];
    # 示例
    ALTER TABLE users MODIFY username VARCHAR(100) NOT NULL COMMENT '用户名';
    • 修改字段名和字段类型
    1
    2
    3
    ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型  [默认值 非空约束] [COMMENT 注释] ;
    # 示例
    ALTER TABLE users CHANGE nickname nick VARCHAR(30);
    • 删除某字段
    1
    2
    3
    ALTER TABLE 表名 DROP 字段名;
    # 示例
    ALTER TABLE users DROP COLUMN email;
    • 修改表名
    1
    2
    3
    ALTER TABLE 表名 RENAME TO 新表名;
    # 示例
    ALTER TABLE tb_user RENAME TO employee;

索引操作

  • 创建索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- UNIQUE 表示添加唯一索引,Index 表示是普通索引
CREATE [UNIQUE] INDEX 索引名 ON 表名(字段名);

-- 或者 使用alter table 语法,就是不同的语法而已,效果一样
alter table 表名 add [UNIQUE] index 索引名(字段名);

# 示例
-- 普通索引
CREATE [UNIQUE] INDEX 索引名称 ON 表名称 (列名称 [(长度)]);
CREATE INDEX idx_username ON users(username);
alter table users add uk_userName(username);

-- 唯一索引
CREATE UNIQUE INDEX 索引名称 ON 表名称 (列名称 [(长度)]);
CREATE UNIQUE INDEX idx_username ON users(username);
alter table users add unique uk_userName(username);

-- 全文索引
CREATE FULLTEXT INDEX 索引名称 ON 表名称 (列名称);

  • 删除索引
1
2
3
4
DROP INDEX 索引名 ON 表名;

-- 或者(删除普通索引和唯一索引的语法是一样的)
alter table 表名 drop index 索引名;
  • 查看索引
1
2
3
4
SHOW INDEX FROM 表名称;

# 示例:
SHOW INDEX FROM users;

视图操作

视图是基于 SQL 查询结果集的虚拟表,它不实际存储数据,而是存储查询定义(也就是三级模式中的外模式)。

视图的优点如下:

1
2
3
4
- 简化复杂查询:将复杂的 SQL 查询封装为简单的表形式(需要注意性能问题)
- 数据安全性:可以限制用户只能看到特定的数据列
- 逻辑独立性:可以屏蔽底层表结构变化对应用的影响
- 数据整合:可以从多个表中整合相关数据
  • 创建视图
1
2
3
4
5
6
7
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
VIEW 视图名称 [(列名称列表)]
AS 查询语句
[WITH [CASCADED | LOCAL] CHECK OPTION];sql

# 示例
create view test_view as select * from user where id <100;
  • 删除视图
1
2
3
4
drop view 视图名称;

# 示例
drop view test_view;

DML语句

MySQL的DML语句(Data Manipulation Language, 数据操纵语言)主要包括插入(INSERT)、更新(UPDATE)、删除(DELETE)和查询(SELECT)语句,以下是按分类列举的示例:

插入 INSERT

  • 插入单条完整记录
1
2
3
INSERT INTO 表名 (列1, 列2, ..., 列n) VALUES (值1, 值2, ..., 值n);
# 示例
INSERT INTO students (student_id, name, age, gender) VALUES (1, '张三', 18, '男');
  • 插入多条记录
1
2
3
4
5
6
7
8
9
10
11
INSERT INTO 表名 (列1, 列2, ..., 列n) 
VALUES
(值1a, 值2a, ..., 值na),
(值1b, 值2b, ..., 值nb),
...;

# 示例
INSERT INTO students (student_id, name, age, gender)
VALUES
(2, '李四', 19, '男'),
(3, '王五', 20, '女');
  • 插入单条部分记录(省略列名,值顺序与表中列顺序一致)
1
2
3
4
INSERT INTO 表名 VALUES (值1, 值2, ..., 值n);

# 示例
INSERT INTO students VALUES (4, '赵六', 21, '男');
  • 插入结果查询
1
2
3
4
INSERT INTO 表名 (列1, 列2, ..., 列n) SELECT1, 列2, ..., 列n FROM 另一个表名 WHERE 条件;

# 示例
INSERT INTO new_students (student_id, name, age) SELECT student_id, name, age FROM students WHERE age > 18;

更新 UPDATE

  • 更新单个字段
1
2
3
UPDATE 表名 SET 列名 = 新值 WHERE 条件;
# 示例
UPDATE students SET age = 20 WHERE student_id = 1;
  • 更新多个字段
1
2
3
UPDATE 表名 SET1 = 新值1, 列2 = 新值2, ..., 列n = 新值n WHERE 条件;
# 示例
UPDATE students SET name = '张三丰', age = 25 WHERE student_id = 1;
  • 无条件更新(全表更新)
1
2
3
UPDATE 表名 SET 列名 = 新值;
# 示例
UPDATE students SET gender = '男';

删除 DELETE

  • 条件删除
1
2
3
DELETE FROM 表名 WHERE 条件;
# 示例
DELETE FROM students WHERE age < 18;
  • 删除所有数据
1
2
3
DELETE FROM 表名;
# 示例
DELETE FROM students;

查询 SELECT

查询是数据库操作的重点,为了更好的说明,我们先来新建两张表并插入几条测试数据

示例数据表
  • users(用户表)
id name age gender city email phone register_time
1 张三 22 北京 zhangsan@test.com 13800138 2023-01-15 08:30:00
2 李四 28 上海 lisi@test.com NULL 2023-02-20 10:15:00
3 王五 25 广州 NULL 13900139 2023-03-05 14:20:00
4 赵六 30 北京 zhaoliu@test.com 13700137 2023-01-10 09:45:00
5 孙七 23 深圳 sunqi@test.com 13600136 2023-02-18 16:00:00
6 周八 35 上海 zhouba@test.com 13500135 2022-12-05 11:30:00
7 吴九 19 广州 wujiu@test.com NULL 2023-03-20 15:10:00
  • orders(订单表)
order_id user_id product_name price order_time
101 1 手机 3999 2023-01-20 10:00:00
102 1 耳机 799 2023-02-05 14:30:00
103 4 电脑 5999 2023-01-12 09:15:00
104 5 平板 2999 2023-02-25 16:40:00
105 6 手表 1599 2022-12-10 11:20:00
106 NULL 键盘 299 2023-03-01 08:50:00
  • 创建表结构及插入数据的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
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age TINYINT UNSIGNED,
gender ENUM('男', '女') NOT NULL,
city VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
register_time DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO users (id, name, age, gender, city, email, phone, register_time) VALUES
(1, '张三', 22, '男', '北京', 'zhangsan@test.com', '13800138', '2023-01-15 08:30:00'),
(2, '李四', 28, '男', '上海', 'lisi@test.com', NULL, '2023-02-20 10:15:00'),
(3, '王五', 25, '女', '广州', NULL, '13900139', '2023-03-05 14:20:00'),
(4, '赵六', 30, '男', '北京', 'zhaoliu@test.com', '13700137', '2023-01-10 09:45:00'),
(5, '孙七', 23, '女', '深圳', 'sunqi@test.com', '13600136', '2023-02-18 16:00:00'),
(6, '周八', 35, '男', '上海', 'zhouba@test.com', '13500135', '2022-12-05 11:30:00'),
(7, '吴九', 19, '女', '广州', 'wujiu@test.com', NULL, '2023-03-20 15:10:00');

CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
order_time DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO orders (order_id, user_id, product_name, price, order_time) VALUES
(101, 1, '手机', 3999.00, '2023-01-20 10:00:00'),
(102, 1, '耳机', 799.00, '2023-02-05 14:30:00'),
(103, 4, '电脑', 5999.00, '2023-01-12 09:15:00'),
(104, 5, '平板', 2999.00, '2023-02-25 16:40:00'),
(105, 6, '手表', 1599.00, '2022-12-10 11:20:00'),
(106, NULL, '键盘', 299.00, '2023-03-01 08:50:00');
基础查询关键字
SELECT + FROM
  • 解释:
    • SELECT:指定要查询的列(投影操作),SELECT * 表示查询所有列。
    • FROM :指定数据来源的表(或视图、子查询等)
  • 示例:
1
2
3
4
5
-- 查询 users 表中所有列
SELECT * FROM users;

-- 查询 users 表中的 id、name、age 列
SELECT id, name, age FROM users;
条件筛选关键字
WHERE
  • 解释:用于过滤行,只返回满足条件的记录。支持比较运算符(=>< 等)、逻辑运算符(ANDOR)、通配符(LIKE)等。

    • 支持逻辑运算符(ANDOR)用于组合多个条件:
      • AND:需同时满足所有条件才返回记录(逻辑 “与”);
      • OR:只要满足任一条件就返回记录(逻辑 “或”);
  • 示例

    1
    2
    3
    4
    5
    6
    -- 查询 age 大于 18 且 gender 为 '男' 的用户
    SELECT name, age FROM users
    WHERE age > 18 AND gender = '男';

    -- 查询 name 以 '张' 开头的用户
    SELECT name FROM users WHERE name LIKE '张%';
BETWEEN ... AND ...
  • 解释:范围查询:筛选列值在指定范围内的记录(包含边界值),一般用于数字类型和日期类型字段的查询

  • 示例

    1
    2
    -- 查询 age 在 20 到 30 之间的用户
    SELECT name, age FROM users WHERE age BETWEEN 20 AND 30;
IN
  • 解释:判断列值是否在指定的列表中,简化多个 OR 条件。

  • 示例

    1
    2
    -- 查询来自 '北京'、'上海'、'广州' 的用户
    SELECT name, city FROM users WHERE city IN ('北京', '上海', '广州');
IS NULL / IS NOT NULL
  • 解释:判断列值是否为 NULL(注意:NULL 表示无值,不能用 = 比较)。

  • 示例

    1
    2
    3
    4
    5
    -- 查询未填写邮箱的用户
    SELECT name FROM users WHERE email IS NULL;

    -- 查询已填写手机号的用户
    SELECT name FROM users WHERE phone IS NOT NULL;
排序关键字
ORDER BY
  • 解释:对查询结果按指定列排序,默认升序(ASC),可指定降序(DESC)。

  • 示例

    1
    2
    3
    4
    5
    -- 按 age 升序排列(默认),若年龄相同则按注册时间降序
    SELECT name, age, register_time FROM users
    ORDER BY age, register_time DESC;

    -- 结果先按 age 从小到大排序,年龄相同的用户再按 register_time 从新到旧排序。示例中所有用户年龄均不同,直接按年龄升序排列
聚合与分组关键字
聚合函数
  • 解释:对一组数据进行计算并返回单一结果

    • COUNT(column):统计非 NULL 值的行数;
    • SUM(column):计算列值总和;
    • AVG(column):计算列值平均值;
    • MAX(column) / MIN(column):获取列的最大值/最小值。
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    -- 统计用户总数
    -- 注意:COUNT(column):仅统计该列非 NULL 值的行数; COUNT(*) 统计所有行(包括 NULL值的行数)
    SELECT COUNT(*) AS total_users FROM users;

    -- 计算所有商品的平均价格
    SELECT AVG(price) AS avg_price FROM orders;

    -- 获取最大年龄和最小年龄
    SELECT MAX(age) AS max_age, MIN(age) AS min_age FROM users;
GROUP BY
  • 解释:将结果按指定列分组,通常与聚合函数搭配使用,对每组数据进行聚合计算。

  • 示例

    1
    2
    -- 按 gender 分组,统计每组的用户数量
    SELECT gender, COUNT(*) AS user_count FROM users GROUP BY gender;
HAVING
  • 解释:对 GROUP BY 分组后的结果进行筛选(WHERE 用于分组前筛选行,HAVING 用于分组后筛选组)。

  • 示例

    1
    2
    3
    4
    5
    -- 按 city 分组,筛选出用户数 > 1 的城市
    SELECT city, COUNT(*) AS user_count
    FROM users
    GROUP BY city
    HAVING user_count > 1;
限制与偏移关键字
LIMIT
  • 解释:限制返回的行数,常用于分页查询。语法:LIMIT 起始位置, 行数(起始位置默认从0开始)。

  • 示例

    1
    2
    3
    4
    5
    -- 返回前 3 条用户记录
    SELECT name, age FROM users LIMIT 3;

    -- 分页查询:从第 3 条开始(起始位置2),返回 2 条(第4-5条)
    SELECT name, age FROM users LIMIT 2, 2;
连接查询关键字
ON(连接条件)
  • 解释:在连接查询(JOIN,默认就是 inner join)中用于指定表之间的关联条件,定义两表如何建立连接关系WHERE 不同,ON 是专门用于连接时的条件筛选,仅作用于关联的表。

  • 示例:通过 users.id = orders.user_id 关联两表,查询用户姓名和对应的订单商品:

    1
    2
    3
    4
    -- 查询用户及其订单信息(只显示用户有订单,且订单的用户也存在的 数据)
    SELECT u.name, o.product_name
    FROM users u
    JOIN orders o ON u.id = o.user_id;
INNER JOIN(内连接)
  • 解释只返回两表中匹配条件的行(交集)

  • 示例

    1
    2
    3
    4
    -- 查询用户及其订单信息(只显示用户有订单,且订单的用户也存在的 数据)
    SELECT u.name, o.product_name
    FROM users u
    INNER JOIN orders o ON u.id = o.user_id;
LEFT JOIN(左连接)
  • 解释返回左表所有行,右表中无匹配的行用 NULL 填充。

  • 示例

    1
    2
    3
    4
    5
    6
    -- 查询所有用户及其订单(包括无订单的用户)
    SELECT u.name, o.order_id
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id;

    -- 返回所有7个用户,有订单的显示订单ID,无订单的 order_id 为 NULL(李四、王五、吴九无订单)

RIGHT JOIN(右连接)

  • 解释返回右表所有行,左表中无匹配的行用 NULL 填充(与左连接相反)。

  • 示例

    1
    2
    3
    4
    -- 查询所有订单及其对应的用户(包括无用户信息的订单)
    SELECT o.order_id, u.name
    FROM users u
    RIGHT JOIN orders o ON u.id = o.user_id;
UNION(合并+去重)
  • 解释:用于合并两个或多个 SELECT 语句的结果集,要求各查询返回的列数、列顺序和数据类型必须一致。合并结果集并去除重复记录(相当于对合并后的数据进行去重);

  • 示例

    1
    2
    3
    4
    -- 合并北京和上海的用户姓名(去重,实际两城市无重名)
    SELECT name FROM users WHERE city = '北京'
    UNION
    SELECT name FROM users WHERE city = '上海';
UNION ALL(合并)
  • 解释:仅合并结果集,保留所有记录(包括重复项),执行效率高于 UNION(因无需去重)。

  • 示例:

    1
    2
    3
    4
    -- 合并北京和上海的用户姓名(保留重复,实际无重复)
    SELECT name FROM users WHERE city = '北京'
    UNION ALL
    SELECT name FROM users WHERE city = '上海';
其他常用关键字
DISTINCT
  • 解释:去除查询结果中的重复行。

  • 示例

    1
    2
    -- 查询所有不重复的城市
    SELECT DISTINCT city FROM users;
AS
  • 解释:为列或表指定别名,简化查询语句。

  • 示例

    1
    2
    3
    4
    -- 为表和列起别名
    SELECT u.name AS 用户名, o.product_name AS 商品名
    FROM users u, orders o
    WHERE u.id = o.user_id;
MySQL 关键字执行顺序

MySQL 执行 SQL 语句时,会按照特定的逻辑顺序解析和执行关键字,而非语句中书写的顺序。理解这一顺序有助于优化查询和排查问题。以下是核心关键字的执行流程(从先到后)。

执行流程
  1. FROM
  • 首先确定查询的数据源,即指定要操作的表(或视图、子查询等)。
  • 若涉及多表连接(如 JOIN),会在此阶段进行表的关联,生成临时的 “连接结果集”。
  1. ON
  • 针对多表连接(JOIN),ON 用于指定表之间的关联条件,筛选符合条件的连接记录,仅作用于关联的表。
  1. JOIN
  • 根据 JOIN 类型(INNER JOINLEFT JOIN 等),结合 ON 条件,生成最终的连接结果集(包含匹配的记录,左连接 / 右连接会保留主表未匹配的记录)。
  1. WHERE
  • FROMJOIN 生成的临时结果集进行筛选,保留满足条件的记录。
  • 注意WHERE 中不能使用 SELECT 中定义的别名,也不能使用聚合函数(如 SUM()COUNT()),因为此时聚合尚未执行。
  1. GROUP BY
  • WHERE 筛选后的结果集按照指定字段分组,相同分组的记录会被合并为一组。
  • 分组后,后续的聚合函数(如 SUMAVG)会基于每组数据计算结果。
  1. HAVING
  • GROUP BY 分组后的结果进行二次筛选,可使用聚合函数(因分组已完成)。
  • WHERE 的区别:HAVING 作用于分组后的数据,WHERE 作用于分组前的原始数据。
  1. SELECT
  • 从前面步骤处理后的结果集中,提取指定的列(或计算字段、别名等)。
  • 此时可以使用 AS 定义别名,供后续步骤(如 ORDER BY)使用,注意在mysql对此做了扩展,字段别名也允许在GROUP By + HAVING中使用。
  1. DISTINCT
  • SELECT 提取的结果进行去重,保留唯一的记录(仅在指定 DISTINCT 时执行)。
  1. ORDER BY
  • 按照指定字段(或表达式)对结果集进行排序(升序 ASC 或降序 DESC)。
  • 可以使用 SELECT 中定义的别名,也可以使用列的序号(不推荐,可读性差)。
  1. LIMIT / OFFSET
  • 最后对排序后的结果集进行截取,返回指定数量的记录(如分页查询)。
示例

以下 SQL 语句的书写顺序与执行顺序对比:

1
2
3
4
5
6
7
8
9
SELECT dept, AVG(salary) AS avg_salary  -- 7. SELECT(定义别名)
FROM employees -- 1. FROM(指定表)
INNER JOIN departments -- 3. JOIN(关联表)
ON employees.dept_id = departments.id -- 2. ON(关联条件)
WHERE hire_year > 2020 -- 4. WHERE(筛选原始数据)
GROUP BY dept -- 5. GROUP BY(分组)
HAVING avg_salary > 5000 -- 6. HAVING(筛选分组结果)
ORDER BY avg_salary DESC -- 9. ORDER BY(排序)
LIMIT 10; -- 10. LIMIT(截取结果)
注意
  • 别名使用范围SELECT 中定义的别名可在 ORDER BYHAVING 中使用,但不能在 WHERE中使用(因执行顺序在前,注意:标准sql别名也不允许在 GROUP BY + HAVING中使用,只是mysql做了扩展而已: 有兴趣可研读 MySQL官网)。
  • 聚合函数位置:聚合函数(如 SUM)只能在 SELECTHAVING 中使用,不能在 WHERE 中使用(WHERE 执行时未分组,无法聚合)。
  • 效率影响WHEREHAVING 更早执行,尽量用 WHERE 筛选原始数据以减少分组 / 聚合的计算量,提升效率。

DCL语句

MySQL的DCL语句(DCL,Data Control Language,数据控制语言)主要分为两类:用户管理语句和权限控制语句。以下是按分类列举的MySQL DCL语句,以及对可控制的权限内容的介绍和示例。

用户管理语句
  • 查询用户
1
SELECT * FROM mysql.user;
  • 创建用户
1
2
3
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
# 示例
CREATE USER 'newuser'@'%' IDENTIFIED BY 'password';
  • 修改用户密码
1
2
3
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
# 示例
ALTER USER 'newuser'@'%' IDENTIFIED BY 'newpassword';
  • 删除用户
1
2
3
DROP USER '用户名'@'主机名';
# 示例
DROP USER 'newuser'@'%';
权限控制语句
  • 查询权限
1
2
3
SHOW GRANTS FOR '用户名'@'主机名';
# 示例
SHOW GRANTS FOR 'newuser'@'%';
  • 授予权限
1
2
3
4
GRANT privileges ON database.table TO 'username'@'host' [WITH GRANT OPTION];
# 示例
GRANT SELECT ON testdb.* TO 'newuser'@'%';
GRANT SELECT, INSERT ON testdb.testtable TO 'newuser'@'%' WITH GRANT OPTION;
  • 撤销权限
1
2
3
REVOKE privileges ON database.table FROM 'username'@'host';
# 示例
REVOKE SELECT ON testdb.* FROM 'newuser'@'%';