复习一下SQL的语法

一、先讲一讲SQL的分类

  1. DDL (Data Definition Language) 数据定义语言,用来定义数据库对象(数据库,表,字段)。

  2. DML (Data Manipulation Language) 数据操作语言,用来对数据库中的数据进行增删改。

  3. DQL (Data Query Language) 数据查询语言,用来查询数据库中的表记录。

  4. DCL (Data Control Language) 数据控制语言,用来创建数据库用户、控制数据库的访问权限。

OK,讲完了基础分类,下面逐个逐个进行讲解。

二、DDL语句—-数据定义语言,用来定义数据库对象(数据库,表,字段)

2.1、数据库生命周期管理(对数据库的增删改)

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建指定字符集的数据库
CREATE DATABASE ecommerce
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

-- 修改数据库配置(MySQL 8.0+)
ALTER DATABASE ecommerce
CHARACTER SET = utf8mb4
READ ONLY = 0;

-- 级联删除数据库(删库了记得跑了)
DROP DATABASE legacy_system CASCADE;

2.2、对表结构的掌控—-建表、改表、删除表

2.2.1、创建表
1
2
3
4
5
6
7
8
9
10
11
12
13
REATE TABLE users (
user_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
username VARCHAR(50) NOT NULL UNIQUE CHECK(LENGTH(username) >= 5),
email VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_active BIT DEFAULT 1,
INDEX idx_email (email),
FULLTEXT INDEX ft_username (username)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
AUTO_INCREMENT=1001
COMMENT='用户主表';
2.2.2、修改表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 添加带有外键约束的列
ALTER TABLE orders
ADD COLUMN user_id INT UNSIGNED NOT NULL,
ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(user_id)
ON DELETE CASCADE;

-- 修改列类型并保持数据完整性
ALTER TABLE products
MODIFY COLUMN price DECIMAL(12,2) UNSIGNED NOT NULL
AFTER product_name;

-- 创建组合索引
CREATE INDEX idx_order_status
ON orders (order_status, payment_status);
2.2.3、删除表
1
2
3
4
5
6
7
8
-- 删除名为users的表
DROP TABLE users;

-- 删除名为orders的表
DROP TABLE orders;

-- 删除缩影
DROP INDEX idx_order_status;

三、DML语句—-数据操作语言,用来对数据库中的数据进行增删改。

3.1、增加数据

1
2
3
4
5
6
7
8
9
10
11
-- 向users表中插入一条记录
INSERT INTO users (name, email, age) VALUES ('John Doe', 'john.doe@example.com', 30);

-- 向users表中插入多条记录
INSERT INTO users (name, email, age) VALUES
('Jane Smith', 'jane.smith@example.com', 25),
('Robert Brown', 'robert.brown@example.com', 40);

-- 向orders表中插入一条记录
INSERT INTO orders (user_id, amount) VALUES (1, 99.99);

3.2、删除数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 删除表中所有数据
DELETE FROM table_name;

-- 删除满足特定条件的数据
DELETE FROM 表名 WHERE 条件;

-- 例1 删除特定 ID 的记录
DELETE FROM employees WHERE employee_id = 1001;

-- 例2 删除特定日期的记录
DELETE FROM orders WHERE order_date = '2023-10-01';

-- 例3 删除特定范围内的记录
DELETE FROM products WHERE price BETWEEN 100 AND 200;

3.3、修改数据

1
2
3
4
5
6
7
8
9
10
11
12
-- 将 employees 表中 employee_id 为 1001 的员工的工资更新为 50000。
UPDATE employees
SET salary = 50000
WHERE employee_id = 1001;
-- 将 employees 表中 department_id 为 10 的所有员工的工资提高 10%。
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;
-- 将 employees 表中 employee_id 为 1002 的员工的工资更新为 60000,并将职位更新为 'Senior Engineer'。
UPDATE employees
SET salary = 60000, job_title = 'Senior Engineer'
WHERE employee_id = 1002;

四、DCL —-数据控制语言,用来创建数据库用户、控制数据库的访问权限

4.1、授予权限

1
2
3
4
5
6
7
8
-- 授予用户 'john' 在 'employees' 数据库上的 SELECT 和 INSERT 权限
GRANT SELECT, INSERT ON employees.* TO 'john'@'localhost';

-- 授予用户 'john' 在 'employees' 表上的 UPDATE 权限
GRANT UPDATE ON employees.employees TO 'john'@'localhost';

-- 授予用户 'john' 在数据库上的 CREATE 和 DROP 权限
GRANT CREATE, DROP ON *.* TO 'john'@'localhost';

4.2、撤销权限

1
2
3
4
5
6
7
8
-- 撤销用户 'john' 在 'employees' 数据库上的 INSERT 权限
REVOKE INSERT ON employees.* FROM 'john'@'localhost';

-- 撤销用户 'john' 在 'employees' 表上的 UPDATE 权限
REVOKE UPDATE ON employees.employees FROM 'john'@'localhost';

-- 撤销用户 'john' 的 CREATE 权限
REVOKE CREATE ON *.* FROM 'john'@'localhost';

这个简单看看就好,交给DBA去吧。接下来才是重头戏 **DQL **登场了

五、DQL —- 数据查询语言,用来查询数据库中的表记录

🆗我们由浅入深 ,从易到难慢慢来

5.1、基本语法

1
2
3
4
5
6
7
SELECT [DISTINCT] 要查询的字段列表 -- SELECT:指定返回列(必需)
FROM table_name -- FROM:指定数据源表(必需)
[WHERE 查询条件] -- WHERE:行级过滤条件
[GROUP BY 分组字段列表] -- GROUP BY:分组依据
[HAVING 分组后条件列表] -- HAVING:分组后过滤
[ORDER BY 排序字段列表 [ASC|DESC]] -- ORDER BY:结果排序
[LIMIT 分页参数]; -- LIMIT:结果集限制

5.2、聚合函数

函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和

使用语法

1
2
3
4
5
6
7
8
9
10
SELECT 	聚合函数(字段列表) FROM 表名;

-- 常用聚合函数
SELECT
COUNT(*) AS total_orders, -- AS就是起别名
AVG(order_total) AS average_order,
MAX(order_date) AS latest_order,
MIN(ship_date) AS earliest_shipment,
SUM(quantity) AS total_items
FROM orders;

5.3、SELECT子句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 基础查询
SELECT * FROM employees; -- 查询所有列

-- 指定列查询
SELECT employee_id, first_name, salary
FROM employees;

-- 使用表达式
SELECT product_name, unit_price * quantity AS total_price
FROM order_details;

-- 常数列 给查询结果都加上一个属性 status ,值是Active
SELECT 'Active' AS status, employee_name
FROM employees;

5.4、WHERER条件过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 比较运算符
SELECT * FROM products
WHERE price > 100 AND stock_quantity < 50;

-- 范围查询
SELECT * FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-05-01';

-- 模糊匹配
SELECT * FROM customers
WHERE email LIKE '%@gmail.com';

-- 空值检查
SELECT * FROM employees
WHERE department_id IS NULL;

-- 多条件组合
SELECT * FROM products
WHERE (category = 'Electronics' OR price > 500)
AND discontinued = 0;

5.5、结果查询(ORDER BY)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 单列排序
SELECT product_name, unit_price
FROM products
ORDER BY unit_price DESC;

-- 多列排序
SELECT last_name, first_name, hire_date
FROM employees
ORDER BY department_id ASC, hire_date DESC;

-- 表达式排序
SELECT product_id, (unit_price * 0.9) AS discount_price
FROM products
ORDER BY discount_price;

5.6、分页查询(LIMIT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 前N条记录
SELECT * FROM sales
ORDER BY sale_date DESC
LIMIT 10;

-- 分页查询(MySQL语法)
SELECT * FROM customers
LIMIT 20 OFFSET 40; -- 第3页,每页20条

-- 结合WHERE使用
SELECT product_id, product_name
FROM products
WHERE category = 'Books'
ORDER BY publication_date
LIMIT 5;

5.7、分组查询(GROUP BY)

1
2
-- 基本语法
SELECT 字段列表 FROM 表名 [WHERE条件] GROUP BY 分组字段名 [HAVING 分组后过滤条件]

WHERE 与 HAVING的区别
①执行的时机不同:WHERE是分组之前进行过滤,不满足WHERE条件,不参与分组;而HAVING是分组之后对结果进行过滤.
②判断条件不同:where条件不能对聚合函数进行判断,而having可以

加上GROUP BY后与不加GROUP BY的结果有何不同,看了下面的东西,我觉得更能让你了解到GROUP BY的区别

假设我们有如下这个表格:

emp_id name gender salary
1 Alice F 5000
2 Bob M 6000
3 Carol F 5500
4 Dave M 7000

执行如下SQL

1
2
3
SELECT gender, COUNT(*) AS num_employees, AVG(salary) AS avg_salary
FROM emp
GROUP BY gender;

你会得到如下的结果(这是加上了GROUP BY的结果)

gender num_employees avg_salary
F 2 5250
M 2 6500

解释原因:

  • 分组的过程:按照gender列的值将数据分成两组:一组是gender = 'F',另一组是gender = 'M'
  • 聚合计数:count(*)计算每组的员工数量、AVG(salary)计算平均每组工资。

执行以下SQL得到的是不加GROUP BY的结果

1
2
SELECT gender, COUNT(*) AS num_employees, AVG(salary) AS avg_salary
FROM emp;

你会得到以下结果(这是不加GROUP BY的结果)

gender num_employees avg_salary
F 4 5875

解释:

  • gender列的值可能是表中的某个值(具体取决于数据库的实现,可能是第一个值或某个任意值),但这个值没有实际意义,因为没有按性别分组
  • COUNT(*)返回整个表的总行数(4)、AVG(salary)返回整个表的平均工资(5875)。

总结就是:

  • GROUP BY:按照指定的列将数据分成多个组,返回每个组的聚合结果。
  • 不加GROUP BY:将整个表视为一个组,返回整个表的聚合结果。

5.8、排序查询

基本语法

1
SELECT 字段列表 FROM 表名 ORDER BY 字段1,字段2... 排序方法;

排序方式有两种:ASC:升序,DESC:降序。

如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行 排序。

5.9、小试牛刀

看完了基本的查询语法,先来做几个题吧。

image-20250521100626528

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- T1
SELECT * FROM 员工表 WHERE age IN (20,21,22,23);

-- T2
SELECT * FROM 员工表 WHERE gender = '男' AND (age BETWEEN 20 AND 40) AND name REGEXP '^[^,]{3}$';
SELECT * FROM 员工表 WHERE gender = '男' AND (age BETWEEN 20 AND 40) AND name like '___';
-- T3
SELECT gender, COUNT(*) FROM 员工表 WHERE age < 60 GROUP BY gender;

-- T4
SELECT name,age FROM 员工表 WHERE age <= 35 GROUP BY age ASC,入职时间 DESC;

-- T5
SELECT * FROM 员工表 WHERE gender = '男' AND (age BETWEEN 20 AND 40) ORDER BY age,入职时间 LIMIT 5;

5.10、SQL的书写顺序与执行顺序

image-20250521103344404

执行顺序 SQL 关键字 作用
FROM 确定主表,准备数据
ON 连接多个表的条件
JOIN 执行 INNER JOIN / LEFT JOIN 等
WHERE 过滤行数据(提高效率)
GROUP BY 进行分组
HAVING 过滤聚合后的数据
SELECT 选择最终返回的列
DISTINCT 进行去重
ORDER BY 对最终结果排序
LIMIT 限制返回行数

image-20250521103829116

六、一些函数

6.1、聚合函数

  • AVG(表达式) 返回表达式中所有的平均值。仅用于数字列并自动忽略NULL值
  • COUNT(表达式) 返回表达式中非NULL值的数量。可用于数字和字符列。
  • COUNT(*) 返回表中的行数(包括有NULL值的列)。
  • MAX(表达式) 返回表达式中的最大值,忽略NULL值。可用于数字、字符和日期时间列。
  • MIN(表达式) 返回表达式中的最小值,忽略NULL值。可用于数字、字符和日期时间列。
  • SUM(表达式) 返回表达式中所有的总和,忽略NULL值。仅用于数字列。

6.2、字符串函数

函数 作用描述
CONCAT(S1, S2, …, Sn) 字符串拼接,将 S1、S2、…、Sn 拼接成一个字符串
LOWER(str) 将字符串 str 全部转为小写
UPPER(str) 将字符串 str 全部转为大写
LPAD(str, n, pad) 左填充,用字符串 pad 对 str 的左边进行填充,达到 n 个字符长度
RPAD(str, n, pad) 右填充,用字符串 pad 对 str 的右边进行填充,达到 n 个字符长度
TRIM(str) 去掉字符串头部和尾部的空格
SUBSTRING(str, start, len) 返回从字符串 str 从 start 位置起的 len 个长度的字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT CONCAT('Hello', ' ', 'World') AS result;
-- 结果:Hello World
SELECT LOWER('HELLO WORLD') AS result;
-- 结果:hello world
SELECT UPPER('hello world') AS result;
-- 结果:HELLO WORLD
SELECT LPAD('Hello', 10, ' ') AS result;
-- 结果:' Hello'(左边填充5个空格)
SELECT RPAD('Hello', 10, ' ') AS result;
-- 结果:'Hello '(右边填充5个空格)
SELECT TRIM(' Hello World ') AS result;
-- 结果:'Hello World'
SELECT SUBSTRING('Hello World', 7, 5) AS result;
-- 结果:'World'

6.3、数值函数

函数 功能描述
CEIL(x) 向上取整,返回大于或等于x的最小整数
FLOOR(x) 向下取整,返回小于或等于x的最大整数
MOD(x, y) 返回x除以y后的余数
RAND() 返回0到1之间的随机数
ROUND(x, y) 对x进行四舍五入,并保留y位小数
1
2
3
4
5
6
7
8
9
10
SELECT CEIL(5.2) AS ceil_value;
-- 结果:6
SELECT FLOOR(5.9) AS floor_value;
-- 结果:5
SELECT MOD(10, 3) AS mod_value;
-- 结果:1
SELECT RAND() AS random_value;
-- 结果:0.5678(每次执行都会返回不同的随机值)
SELECT ROUND(5.768, 2) AS rounded_value;
-- 结果:5.77

6.4、日期函数

函数 功能描述
CURDATE() 返回当前日期
CURTIME() 返回当前时间
NOW() 返回当前日期和时间
YEAR(date) 获取指定 date 的年份
MONTH(date) 获取指定 date 的月份
DAY(date) 获取指定 date 的日期
DATE_ADD(date, INTERVAL expr type) 返回一个日期/时间值加上一个时间间隔 expr 后的时间值
DATEDIFF(date1, date2) 返回起始时间 date1 和结束时间 date2 之间的天数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT CURDATE() AS current_date;
-- 结果:返回当前日期,例如 '2025-5-12'

SELECT CURTIME() AS current_time;
-- 结果:返回当前时间,例如 '15:30:45'

SELECT NOW() AS current_datetime;
-- 结果:返回当前日期和时间,例如 '2025-5-12 15:30:45'

SELECT YEAR('2025-5-12') AS year_value;
-- 结果:2025

SELECT MONTH('2025-5-12') AS month_value;
-- 结果:5

SELECT DAY('2025-5-12') AS day_value;
-- 结果:12

SELECT DATE_ADD('2025-5-12', INTERVAL 5 DAY) AS new_date;
-- 结果:2025-5-17(在原日期基础上加5天)

SELECT DATEDIFF('2025-5-12', '2025-5-05') AS date_difference;
-- 结果:7(两个日期之间的天数差)

6.5、更多函数

函数太多了,推荐你要用什么就查什么。
推荐阅读:SQL函数大全及示例汇总_sql函数大全及举例-CSDN博客

七、多表查询

7.1、笛卡儿积

在多表查询之前,得先要去了解以下笛卡尔积啊。

笛卡尔积是指在数学中,两个集合,A集合和B集合的所有组合情况(在多表查询时,需要消除无效的笛卡尔积)image-20250521113858241

总体看一下多表查询的分类image-20250521114221127

7.2、内连接查询————只返回满足连接条件的记录

内连接查询的是两张表的交集的部分,即下图中绿色区域

image-20250521114507510

内连接有两种写法:

1
2
3
4
5
6
7
8
9
-- 显式内连接
SELECT e.name, d.dname
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;

-- 隐式内连接
SELECT e.name, d.dname
FROM employees e, departments d
WHERE e.dept_id = d.id;

7.3、外连接

7.3.1、左外连接

保留左表全部记录,右表无匹配时显示NULL

1
2
3
SELECT e.name, d.dname
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;
7.3.2、右外连接

保留右表全部记录,左表无匹配时显示NULL

1
2
3
SELECT e.name, d.dname
FROM departments d
RIGHT JOIN employees e ON d.id = e.dept_id;
7.3.3、自连接

顾名思义,就是自己连接自己,也就是把一张表连接查询多次

1
2
3
4
-- 查询员工及其经理
SELECT e1.name AS employee, e2.name AS manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id;

普通员工和领导其实都属于员工,都在员工表当中,每个员工又有一项manager_id记录这他的领导的id值
用内连接

1
2
3
select a.name , b.name 
from emp as a , emp as b
where a.managerid = b.id;

7.4 、联合查询

联合查询就是将多次查询的结果合并起来,形成一个新的结果集

使用联合查询有一个条件就是说,几次查询到的结果集的列数必须保持一致,字段类型也要保持一直。

不能说你两次查询到的结果集,都是两列,但是一个结果字段类型是:age、name,一个结果的字段类型是:gender、salary。

涉及到两个关键在unionunion all

union all 是将几次查询得到的结果集直接合并,union是将查询到的结果集合并再去重

1
2
3
SELECT 字段列表 FROM 表A....
UNION[ALL]
SELECT 字段列表 FROM 表B....

将第一条sql和第二条sql查询到的结果进行合并

7.5、子查询

SQL语句中嵌套SELECT语句,称为嵌套查询,又称为子查询。

1
SELECT * FROM t1 WHERE column = (SELECT cloumn1 FROM t2)

注意:子查询的外部可以是INSERTUPDATEDELETESELECT中的任意一个,这里只是拿SELECT举个栗子

子查询又分为了

  • 标量子查询(子查询结果为单个值)
  • 列子查询(子查询结果为一列)
  • 行子查询(子查询结果为一行)
  • 表子查询(子查询结果为多行多列)
7.5.1、标量子查询

子查询返回的结果是单个值(数字、字符串、日期等)

1
2
3
-- 查询高于平均工资的员工
SELECT name FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
7.5.2、列子查询

子查询返回的结果是一列(可以是多行)

常用的操作符如下

操作符 描述
IN 在指定的集合范围之内,多选一
NOT IN 不在指定的集合范围之内
ANY 子查询返回列表中,有任意一个满足即可
SOME 与ANY等同,使用SOME的地方都可以使用ANY
ALL 子查询返回列表的所有值都必须满足
1
2
3
4
5
-- 查询研发部所有员工
SELECT name FROM employees
WHERE dept_id IN (
SELECT id FROM departments WHERE dname = '研发部'
);
7.5.3、行子查询

返回单行多列结果

1
2
3
4
5
6
7
-- 查询与张三同部门同职位的员工
SELECT * FROM employees
WHERE (dept_id, position) = (
SELECT dept_id, position
FROM employees
WHERE name = '张三'
);

7.5.4、表子查询
返回多行多列结果集

1
2
3
4
5
6
7
-- 作为临时表使用
SELECT * FROM (
SELECT dept_id, AVG(salary) avg_sal
FROM employees
GROUP BY dept_id
) AS dept_sal
WHERE avg_sal > 10000;

八、最后做题

image-20250521145005151

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
-- T1
SELECT name, age, position, department_id FROM employees;
-- T2
SELECT name, age, position, department_id FROM employees WHERE age < 30;
-- T3
SELECT department_id, department_name
FROM departments
WHERE department_id
IN (SELECT DISTINCT department_id FROM employees);
-- T4
SELECT e.name, e.age, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE e.age > 40;
-- T5
SELECT name, salary,
CASE
WHEN salary > 10000 THEN 'High'
WHEN salary BETWEEN 5000 AND 10000 THEN 'Medium'
ELSE 'Low'
END AS salary_level
FROM employees;
-- T6
SELECT e.name, e.age, e.position, e.salary,
CASE
WHEN e.salary > 10000 THEN 'High'
WHEN e.salary BETWEEN 5000 AND 10000 THEN 'Medium'
ELSE 'Low'
END AS salary_level
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.department_name = '研发部';

.。。。。。。后面懒得写了。。。。。。。。

写SQL还是一直磕磕绊绊

😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭