SQL执行顺序:

第1步:FROM - 找到数据源

FROM users u

MySQL首先要知道数据从哪里来,所以第一步是确定表和给表起别名。

这一步做了什么?

  • 找到指定的表
  • 为表创建别名(如果有的话)
  • 准备读取数据

第2步:JOIN - 连接多张表

FROM users u
JOIN orders o ON u.id = o.user_id

如果查询涉及多张表,MySQL会根据JOIN条件将它们连接起来。

常见的JOIN类型:

  • INNER JOIN:只返回两表都有的数据
  • LEFT JOIN:返回左表所有数据,右表没有则为NULL
  • RIGHT JOIN:返回右表所有数据,左表没有则为NULL
-- 示例:查询用户及其订单信息
SELECT u.name, o.order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

第3步:WHERE - 过滤不需要的行

WHERE u.age > 18 AND u.status = 'active'

在分组之前,MySQL会根据WHERE条件过滤掉不符合条件的行。

注意:WHERE不能使用聚合函数!

-- ❌ 错误写法
SELECT name, COUNT(*) as cnt
FROM users
WHERE COUNT(*) > 5; -- 报错!

-- ✅ 正确写法
SELECT name, COUNT(*) as cnt
FROM users
GROUP BY name
HAVING COUNT(*) > 5; -- 用HAVING

第4步:GROUP BY - 数据分组

GROUP BY u.department, u.position

将数据按照指定的列进行分组,为聚合函数做准备。

分组示例:

-- 按部门统计员工数量
SELECT department, COUNT(*) as employee_count
FROM users
WHERE status = 'active'
GROUP BY department;

第5步:HAVING - 过滤分组

HAVING COUNT(*) > 10

HAVING是对分组后的结果进行过滤,可以使用聚合函数。

WHERE vs HAVING 对比:

条件 WHERE HAVING
执行时机 分组前 分组后
过滤对象 分组
能否使用聚合函数
-- 找出订单数量超过10的用户
SELECT user_id, COUNT(*) as order_count
FROM orders
WHERE order_status = 'completed' -- 先过滤已完成的订单
GROUP BY user_id
HAVING COUNT(*) > 10; -- 再过滤订单数量超过10的用户

第6步:SELECT - 选择要显示的列

SELECT u.name, u.age, COUNT(o.id) as order_count

到了这一步,MySQL才开始处理SELECT子句,选择要显示的列。

这就是为什么WHERE不能使用SELECT中定义的别名!

-- ❌ 错误:WHERE执行在SELECT之前
SELECT name, age * 2 as double_age
FROM users
WHERE double_age > 50; -- double_age还不存在!

-- ✅ 正确写法
SELECT name, age * 2 as double_age
FROM users
WHERE age * 2 > 50;

第7步:DISTINCT - 去除重复

SELECT DISTINCT department
FROM users;

如果使用了DISTINCT,MySQL会去除重复的行。

第8步:ORDER BY - 排序

ORDER BY u.age DESC, u.name ASC

对最终结果进行排序。

ORDER BY可以使用SELECT中的别名:

-- ✅ 正确:ORDER BY执行在SELECT之后
SELECT name, age * 2 as double_age
FROM users
ORDER BY double_age DESC; -- 可以使用别名

第9步:LIMIT - 限制结果数量

LIMIT 10 OFFSET 20

最后一步,限制返回的结果数量。

💡 实战案例:执行顺序的应用

让我们通过一个实际案例来理解执行顺序:

-- 需求:查询每个部门中年龄大于25岁的员工数量,
-- 只显示员工数量超过5人的部门,按员工数量降序排列

SELECT
department,
COUNT(*) as employee_count
FROM users
WHERE age > 25
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY employee_count DESC;

执行过程分析:

  1. FROM users - 确定数据源
  2. WHERE age > 25 - 过滤年龄大于25的员工
  3. GROUP BY department - 按部门分组
  4. HAVING COUNT(*) > 5 - 过滤员工数量超过5的部门
  5. SELECT department, COUNT(*) - 选择要显示的列
  6. ORDER BY employee_count DESC - 按员工数量降序排列

SQL操作/关键字

SQL操作

JOIN详解

1. JOIN 的分类和对比

![image-20251201134425545](/Users/mag1code/Library/Application Support/typora-user-images/image-20251201134425545.png)

SELECT DISTINCT

SELECT DISTINCT column1, column2, ...FROM table_name;

distinct后可以接多个列,返回这些列不全重复的值。

UNION

SQL UNION 操作符用于组合两个或多个 SELECT 语句的结果集。

  • UNION 中的每个 SELECT 语句都必须具有相同数量的列。
  • 列也必须具有相似的数据类型。
  • 每个 SELECT 语句中的列也必须以相同的顺序排列。

UNION 语法

SELECT column_name(s) FROM table1
并集
SELECT column_name(s) FROM table2;

UNION ALL 语法

默认情况下,UNION 操作符只选择不重复的值。要允许重复值,请使用 UNION ALL

ROUND() 函数

ROUND() 函数用于把数值字段舍入为指定的小数位数。

SQL ROUND() 语法

**SELECT** ROUND(column_name,decimals) **FROM** **TABLE_NAME**;

CASE WHEN THEN

CASE 表达式会逐一检查条件,并在第一个条件满足时返回值(类似于 if-then-else 语句)。因此,一旦某个条件为真,它就会停止读取并返回结果。如果没有条件为真,它将返回 ELSE 子句中的值。如果没有 ELSE 部分且没有任何条件为真,则返回 NULL。

CASE 的本质

CASE 是一个条件表达式,它在每一行数据上进行计算,根据条件返回不同的值。

执行流程

 SELECT

CASE

WHEN age < 25 OR age IS NULL THEN '25岁以下'

WHEN age >= 25 THEN '25岁及以上'

END age_cut,

COUNT(*) number

FROM user_profile

GROUP BY age_cut;

底层执行:

  1. 扫描表 - 逐行读取 user_profile 的所有行
  2. 计算 CASE - 对每一行执行 CASE 逻辑:

行1: age=20 → CASE判断 → ‘25岁以下’

行2: age=30 → CASE判断 → ‘25岁及以上’

行3: age=NULL → CASE判断 → ‘25岁以下’

行4: age=26 → CASE判断 → ‘25岁及以上’

  1. 创建临时结果集 - 产生一个临时列:

age | CASE结果

-——–

20 | ‘25岁以下’

30 | ‘25岁及以上’

NULL| ‘25岁以下’

26 | ‘25岁及以上’

  1. GROUP BY - 按这个临时列的值分组
  2. 聚合 - 计算 COUNT(*)

CASE 语法

CASE
WHEN *condition1* THEN *result1*
WHEN *condition2* THEN *result2*
WHEN *conditionN* THEN *resultN*
ELSE *result*
END;

窗口函数

https://www.zhihu.com/tardis/zm/art/92654574?source_id=1005

窗口函数的基本语法如下:

<窗口函数> over (partition by <用于分组的列名>
order by <用于排序的列名>)

那么语法中的<窗口函数>都有哪些呢?

<窗口函数>的位置,可以放以下两种函数:

1) 专用窗口函数,包括后面要讲到的rank, dense_rank, row_number等专用窗口函数。

2) 聚合函数,如sum. avg, count, max, min等

因为窗口函数是对where或者group by子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中

IF函数

IF() 函数在条件为 TRUE 时返回一个值,在条件为 FALSE 时返回另一个值。

语法

IF(condition, value_if_true, value_if_false)

字符串操作

SUBSTRING_INDEX

SUBSTRING_INDEX() 函数在指定分隔符出现次数之前返回字符串的子串。

语法

SUBSTRING_INDEX(string, delimiter, number)

CONCAT/CONCAT_WS

把字符串连接起来。

语法:CONCAT_AS(separator,str1,str2,...)

separator是必要的分隔符,concat中不需要。

FIND_IN_SET

找到字符在一个字符串中的索引位置。

RegExp正则表达式

正则表达式用来匹配文本的特殊的串(字符集合)(匹配文本,将一个模式(正则表达式)与一个文本串进行比较)。

和LIKE相似,但是有不一样的空白元字符:

模式 描述
? 问号可用于与字符匹配 0 次或 1 次。例如,'colou?r' 匹配 color 和 colour。
$/% 匹配输入字符串的结束/开始位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前/后的位置。
. 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像 ‘[.\n]’ 的模式。
[…] 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。
[^…] 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’。
p1|p2|p3 匹配 p1 或 p2 或 p3。例如,’z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,’o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。

eg.phone_number REGEXP "^[0-9]{3}-?[0-9]{3}-?[0-9]{4}$"该表达式反映了

LIKE

一般配合WHERE使用,可在列中搜索指定的模式。

ep:`* WHERE something LIKE “%1_”*…

一般LIKE和会和通配符一起使用,

通配符字符

符号 描述
% 表示零个或多个字符
_ 表示单个字符
[] 表示括号内的任何单个字符 *
^ 表示括号内不在的任何字符 *
- 表示指定范围内的任何单个字符 *
{} 表示任何转义字符 **

数值函数

数值处理

函数 说明
SIN() 正弦
COS() 余弦
TAN() 正切
ABS() 绝对值
SQRT() 平方根
MOD() 余数
EXP() 指数
PI() 圆周率
RAND() 随机数
CELL()/FLOOR() 向上取整/向下取整