适用于 MySQL 8.x 学习与日常开发。本文优先详细介绍高频函数,低频函数只给出用途和基本语法。
本文使用以下标记:
| 标记 | 含义 |
|---|---|
| [SQL] | 属于 SQL 标准,或者具有明确的标准 SQL 形式;主流关系数据库通常提供相同或相近能力 |
| [MySQL] | MySQL 方言提供的函数、别名或特有语法;迁移到其他数据库时通常需要改写 |
需要注意:
NULL、时区、精度和边界情况的处理仍可能不同;CASE、CAST、EXTRACT 等严格来说是 SQL 表达式,不一定属于普通函数,但日常学习时通常会与函数一起讨论;| 分类 | 高频内容 |
|---|---|
| 聚合 | COUNT、SUM、AVG、MIN、MAX、GROUP_CONCAT |
| 字符串 | CONCAT、CHAR_LENGTH、LENGTH、SUBSTRING、TRIM、REPLACE |
| 日期时间 | CURRENT_TIMESTAMP、DATE_ADD、DATE_SUB、DATEDIFF、TIMESTAMPDIFF、DATE_FORMAT |
| 数值 | ROUND、CEIL、FLOOR、ABS、MOD |
NULL 与条件 |
COALESCE、NULLIF、CASE、IF、IFNULL |
| 类型转换 | CAST、CONVERT |
| JSON | JSON_EXTRACT、JSON_SET、JSON_OBJECT、JSON_ARRAYAGG |
| 窗口函数 | ROW_NUMBER、RANK、LAG、LEAD、聚合函数 OVER() |
| 系统信息 | DATABASE、CURRENT_USER、VERSION、LAST_INSERT_ID |
聚合函数把多行数据计算成一个结果。它们经常与 GROUP BY、HAVING 配合使用。
COUNT():统计数量标记:[SQL]
语法:
COUNT(*)
COUNT(expression)
COUNT(DISTINCT expression)
区别:
| 写法 | 含义 |
|---|---|
COUNT(*) |
统计行数,不关心某列是否为 NULL |
COUNT(column) |
只统计该列不是 NULL 的行 |
COUNT(DISTINCT column) |
统计去重后的非 NULL 值数量 |
示例:
SELECT COUNT(*) AS actor_count
FROM actor;
SELECT
COUNT(*) AS total_rows,
COUNT(return_date) AS returned_rows
FROM rental;
return_date 允许为 NULL 时,两者可能不同。
按客户统计租赁次数:
SELECT
customer_id,
COUNT(*) AS rental_count
FROM rental
GROUP BY customer_id
ORDER BY rental_count DESC;
经验:
COUNT(DISTINCT id) 能消除部分重复,但也可能增加计算成本;COUNT(column) 误认为总行数。SUM():求和标记:[SQL]
语法:
SUM(expression)
SUM(DISTINCT expression)
示例:
SELECT SUM(amount) AS total_amount
FROM payment;
按客户统计付款总额:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM payment
GROUP BY customer_id;
没有匹配行时,SUM() 通常返回 NULL,不是 0:
SELECT COALESCE(SUM(amount), 0) AS total_amount
FROM payment
WHERE customer_id = -1;
金额列应使用 DECIMAL,不要依赖浮点数完成精确财务计算。
AVG():平均值标记:[SQL]
语法:
AVG(expression)
AVG(DISTINCT expression)
示例:
SELECT AVG(length) AS average_length
FROM film;
AVG(column) 会忽略 NULL。如果业务要求把缺失值视为零,需要明确写出:
SELECT AVG(COALESCE(score, 0))
FROM exam_result;
但这会改变统计含义,不能为了消除 NULL 随意使用。
MIN()、MAX():最小值与最大值标记:[SQL]
语法:
MIN(expression)
MAX(expression)
示例:
SELECT
MIN(amount) AS minimum_amount,
MAX(amount) AS maximum_amount
FROM payment;
不仅能处理数字,也能处理日期和字符串:
SELECT
MIN(payment_date) AS first_payment_at,
MAX(payment_date) AS last_payment_at
FROM payment;
GROUP_CONCAT():把多行拼接成字符串标记:[MySQL]
语法:
GROUP_CONCAT(
[DISTINCT] expression
[ORDER BY expression]
[SEPARATOR 'separator']
)
查询电影对应的演员姓名:
SELECT
f.film_id,
f.title,
GROUP_CONCAT(
CONCAT(a.first_name, ' ', a.last_name)
ORDER BY a.last_name, a.first_name
SEPARATOR ', '
) AS actors
FROM film AS f
JOIN film_actor AS fa ON fa.film_id = f.film_id
JOIN actor AS a ON a.actor_id = fa.actor_id
GROUP BY f.film_id, f.title;
注意:
group_concat_max_len 限制;CONCAT():拼接字符串标记:[MySQL]
语法:
CONCAT(value1, value2, ...)
示例:
SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM actor;
MySQL 中只要任意参数是 NULL,CONCAT() 就返回 NULL:
SELECT CONCAT('Hello', NULL); -- NULL
需要忽略 NULL 时可以使用 CONCAT_WS()。
CONCAT_WS():使用分隔符拼接标记:[MySQL]
语法:
CONCAT_WS(separator, value1, value2, ...)
示例:
SELECT CONCAT_WS(', ', address, district, postal_code) AS address_text
FROM address;
CONCAT_WS() 会跳过后续参数中的 NULL,但不会自动跳过空字符串。
CHAR_LENGTH() 与 LENGTH()CHAR_LENGTH():[SQL]
LENGTH():[MySQL],在 MySQL 中统计字节数。
语法:
CHAR_LENGTH(string)
LENGTH(string)
示例:
SELECT
CHAR_LENGTH('中国') AS character_count,
LENGTH('中国') AS byte_count;
在 utf8mb4 下通常会得到:
character_count = 2
byte_count = 6
判断用户可见字符数时通常使用 CHAR_LENGTH(),检查存储字节数时才使用 LENGTH()。
LOWER()、UPPER():大小写转换标记:[SQL]
语法:
LOWER(string)
UPPER(string)
示例:
SELECT
LOWER(email) AS normalized_email,
UPPER(first_name) AS upper_name
FROM customer;
是否区分大小写主要受字符集和排序规则影响。不要仅靠查询时 LOWER(email) 保证邮箱唯一,最终仍应设计合适的 MySQL 唯一索引和 collation。
TRIM():去除两端字符标记:[SQL]
常用语法:
TRIM(string)
TRIM([BOTH | LEADING | TRAILING] remove_string FROM string)
示例:
SELECT TRIM(' hello '); -- 'hello'
SELECT TRIM(LEADING '0' FROM '000123'); -- '123'
SELECT TRIM(TRAILING '.' FROM 'abc...'); -- 'abc'
关联函数:
LTRIM(string)
RTRIM(string)
LTRIM()、RTRIM() 在 MySQL 中常用,但可移植性不如标准 TRIM() 写法。
SUBSTRING():截取字符串标记:[SQL],MySQL 同时支持多种简写。
语法:
SUBSTRING(string, start)
SUBSTRING(string, start, length)
SUBSTRING(string FROM start FOR length)
示例:
SELECT SUBSTRING('abcdef', 2, 3); -- 'bcd'
MySQL 字符位置从 1 开始。负数表示从末尾开始:
SELECT SUBSTRING('abcdef', -3); -- 'def'
LEFT()、RIGHT()标记:[MySQL]
语法:
LEFT(string, length)
RIGHT(string, length)
示例:
SELECT LEFT('abcdef', 3); -- 'abc'
SELECT RIGHT('abcdef', 3); -- 'def'
REPLACE():替换字符串标记:[MySQL]
语法:
REPLACE(string, from_string, to_string)
示例:
SELECT REPLACE('2026/08/09', '/', '-');
这是字符串替换函数,不等同于 MySQL 的 REPLACE INTO 写入语句。
LOCATE()、INSTR()、POSITION():查找位置POSITION():[SQL]
LOCATE()、INSTR():[MySQL]
语法:
POSITION(substring IN string)
LOCATE(substring, string [, start])
INSTR(string, substring)
示例:
SELECT POSITION('@' IN 'user@example.com');
SELECT LOCATE('@', 'user@example.com');
找不到时返回 0。
LPAD()、RPAD():补齐长度标记:[MySQL]
语法:
LPAD(string, target_length, pad_string)
RPAD(string, target_length, pad_string)
示例:
SELECT LPAD('123', 6, '0'); -- '000123'
它适合生成展示文本,不代表数据库主键本身应该保存成补零字符串。
| 函数 | 标记 | 用途 |
|---|---|---|
REVERSE(string) |
[MySQL] | 反转字符串 |
REPEAT(string, count) |
[MySQL] | 重复字符串 |
SPACE(count) |
[MySQL] | 生成指定数量空格 |
ASCII(string) |
[MySQL] | 返回首字符 ASCII 值 |
CHAR(number, ...) |
[MySQL] | 从字符编码生成字符串 |
HEX(value)、UNHEX(value) |
[MySQL] | 十六进制编码与解码 |
FIND_IN_SET(value, csv) |
[MySQL] | 在逗号字符串中查找位置,不建议替代关联表 |
标准形式:
CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
标记:[SQL]
MySQL 常用别名:
CURDATE()
CURTIME()
NOW()
标记:[MySQL]
示例:
SELECT
CURRENT_DATE,
CURRENT_TIME,
CURRENT_TIMESTAMP;
NOW() 在同一条语句执行期间通常保持一致。需要明确数据库会话时区,否则 Node.js、MySQL 与用户时区可能产生理解差异。
DATE()、TIME():取日期或时间部分标记:[MySQL]
语法:
DATE(datetime_expression)
TIME(datetime_expression)
示例:
SELECT DATE(payment_date), TIME(payment_date)
FROM payment;
不要习惯性这样查询某一天:
WHERE DATE(payment_date) = '2026-08-09'
对列使用函数可能使普通索引难以直接完成范围定位。更推荐:
WHERE payment_date >= '2026-08-09 00:00:00'
AND payment_date < '2026-08-10 00:00:00'
这就是左闭右开时间范围。
EXTRACT():提取日期部分标记:[SQL]
语法:
EXTRACT(unit FROM datetime_expression)
示例:
SELECT
EXTRACT(YEAR FROM payment_date) AS payment_year,
EXTRACT(MONTH FROM payment_date) AS payment_month
FROM payment;
MySQL 还提供快捷函数:
YEAR(date)
MONTH(date)
DAY(date)
HOUR(datetime)
MINUTE(datetime)
SECOND(datetime)
这些快捷函数标记为 [MySQL]。
DATE_ADD()、DATE_SUB():日期加减标记:[MySQL]
语法:
DATE_ADD(date, INTERVAL value unit)
DATE_SUB(date, INTERVAL value unit)
示例:
SELECT DATE_ADD('2026-08-09', INTERVAL 1 DAY);
SELECT DATE_SUB('2026-08-09', INTERVAL 7 DAY);
SELECT DATE_ADD('2026-08-09 10:30:00', INTERVAL 2 HOUR);
MySQL 也支持运算形式:
date_expression + INTERVAL 1 DAY
date_expression - INTERVAL 1 DAY
高频单位:
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
月份加减要注意月底规则,例如 1 月 31 日加一个月的结果不能简单理解成固定增加 30 天。
DATEDIFF():相差多少天标记:[MySQL]
语法:
DATEDIFF(end_date, start_date)
示例:
SELECT DATEDIFF('2026-08-10', '2026-08-01'); -- 9
DATEDIFF() 只比较日期部分,忽略具体时分秒。
TIMESTAMPDIFF():按指定单位计算差值标记:[MySQL]
语法:
TIMESTAMPDIFF(unit, start_datetime, end_datetime)
示例:
SELECT TIMESTAMPDIFF(
HOUR,
'2026-08-09 08:00:00',
'2026-08-10 10:30:00'
); -- 26
注意参数顺序与 DATEDIFF(end, start) 不完全一样:
DATEDIFF(end, start)
TIMESTAMPDIFF(unit, start, end)
这是很常见的记忆陷阱。
DATE_FORMAT():日期格式化为字符串标记:[MySQL]
语法:
DATE_FORMAT(datetime_expression, format_string)
示例:
SELECT DATE_FORMAT(payment_date, '%Y-%m-%d %H:%i:%s')
FROM payment;
常用格式:
| 格式 | 含义 |
|---|---|
%Y |
四位年份 |
%m |
两位月份 |
%d |
两位日期 |
%H |
24 小时制小时 |
%i |
分钟,注意不是 %m |
%s |
秒 |
按月统计:
SELECT
DATE_FORMAT(payment_date, '%Y-%m') AS payment_month,
SUM(amount) AS total_amount
FROM payment
GROUP BY DATE_FORMAT(payment_date, '%Y-%m');
用于显示时,很多团队更倾向于让数据库返回原始时间,由应用层根据用户语言和时区格式化。用于报表分组时则很常见。
STR_TO_DATE():字符串解析成日期标记:[MySQL]
语法:
STR_TO_DATE(string, format_string)
示例:
SELECT STR_TO_DATE('09/08/2026', '%d/%m/%Y');
不要把它当成接口输入校验的替代品。应用层仍应先校验输入格式,数据库列则应使用真正的 DATE、DATETIME 或 TIMESTAMP 类型。
LAST_DAY():取得月份最后一天标记:[MySQL]
语法:
LAST_DAY(date)
示例:
SELECT LAST_DAY('2026-02-10'); -- '2026-02-28'
标记:[MySQL]
UNIX_TIMESTAMP([datetime])
FROM_UNIXTIME(unix_timestamp [, format])
示例:
SELECT UNIX_TIMESTAMP(CURRENT_TIMESTAMP);
SELECT FROM_UNIXTIME(1786248000);
使用时要明确秒和毫秒的区别:JavaScript Date.now() 返回毫秒,而 Unix 时间戳经常以秒表示。
| 函数 | 标记 | 用途 |
|---|---|---|
DAYOFWEEK(date) |
[MySQL] | 返回星期序号,周日为 1 |
WEEKDAY(date) |
[MySQL] | 返回星期序号,周一为 0 |
DAYOFYEAR(date) |
[MySQL] | 一年中的第几天 |
WEEK(date [, mode]) |
[MySQL] | 周数,受 mode 影响 |
QUARTER(date) |
[MySQL] | 季度 1–4 |
MAKEDATE(year, day) |
[MySQL] | 根据年份和一年中的天数构造日期 |
MAKETIME(hour, minute, second) |
[MySQL] | 构造时间 |
CONVERT_TZ(datetime, from_tz, to_tz) |
[MySQL] | 转换时区,需要时区表支持 |
ROUND():四舍五入标记:[SQL]
语法:
ROUND(number [, decimal_places])
示例:
SELECT ROUND(12.3456, 2); -- 12.35
SELECT ROUND(1234.56, -2); -- 1200
展示金额时可以使用 ROUND(),但数据库字段本身仍应使用正确的 DECIMAL(precision, scale)。
CEIL()、CEILING():向上取整标记:[SQL]
CEIL(number)
CEILING(number)
示例:
SELECT CEIL(3.01); -- 4
FLOOR():向下取整标记:[SQL]
FLOOR(number)
示例:
SELECT FLOOR(3.99); -- 3
ABS():绝对值标记:[SQL]
ABS(number)
MOD():取余数标记:[SQL]
MOD(dividend, divisor)
dividend % divisor
示例:
SELECT MOD(10, 3); -- 1
% 是 MySQL 支持的运算符形式,可移植 SQL 更适合使用 MOD()。
RAND():[MySQL]
RAND([seed])
示例:
SELECT RAND();
随机取记录:
SELECT film_id, title
FROM film
ORDER BY RAND()
LIMIT 1;
ORDER BY RAND() 会为大量候选行生成随机值并排序,大表上成本很高,只适合小数据集或练习。
| 函数 | 标记 | 用途 |
|---|---|---|
POWER(x, y) |
[SQL] | x 的 y 次方 |
SQRT(x) |
[SQL] | 平方根 |
EXP(x) |
[SQL] | e 的 x 次方 |
LN(x) |
[SQL] | 自然对数 |
LOG10(x) |
[SQL] | 以 10 为底的对数 |
SIGN(x) |
[SQL] | 返回 -1、0 或 1 |
PI() |
[MySQL] | 圆周率 |
RADIANS(x) |
[MySQL] | 角度转弧度 |
DEGREES(x) |
[MySQL] | 弧度转角度 |
TRUNCATE(x, d) |
[MySQL] | 截断到 d 位小数,不做四舍五入 |
NULL 与条件处理COALESCE():取得第一个非 NULL 值标记:[SQL]
语法:
COALESCE(value1, value2, ...)
示例:
SELECT COALESCE(address2, '无补充地址')
FROM address;
SELECT COALESCE(SUM(amount), 0)
FROM payment
WHERE customer_id = -1;
它不会把空字符串、0 或 false 当成 NULL。
NULLIF():相等时返回 NULL标记:[SQL]
语法:
NULLIF(value1, value2)
如果两个值相等,返回 NULL;否则返回第一个值。
避免除零:
SELECT total_amount / NULLIF(order_count, 0)
FROM statistics;
CASE:标准条件表达式标记:[SQL]
搜索式语法:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
示例:
SELECT
rental_id,
CASE
WHEN return_date IS NULL THEN '租赁中'
ELSE '已归还'
END AS rental_status
FROM rental;
简单式语法:
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
条件聚合:
SELECT
COUNT(*) AS total_count,
SUM(CASE WHEN return_date IS NULL THEN 1 ELSE 0 END) AS open_count
FROM rental;
IF():MySQL 条件函数标记:[MySQL]
语法:
IF(condition, true_value, false_value)
示例:
SELECT IF(return_date IS NULL, '租赁中', '已归还')
FROM rental;
简单条件可以使用 IF();复杂逻辑和需要可移植性时优先使用 CASE。
IFNULL():MySQL 的二参数空值替代标记:[MySQL]
IFNULL(value, fallback_value)
示例:
SELECT IFNULL(address2, '无补充地址')
FROM address;
可移植形式是:
COALESCE(address2, '无补充地址')
GREATEST()、LEAST()标记:[MySQL],其他数据库也可能支持,但兼容性和 NULL 行为需要确认。
GREATEST(value1, value2, ...)
LEAST(value1, value2, ...)
示例:
SELECT GREATEST(10, 20, 5); -- 20
SELECT LEAST(10, 20, 5); -- 5
MySQL 中任意参数为 NULL 时,结果通常为 NULL。
CAST():标准类型转换标记:[SQL]
语法:
CAST(expression AS type)
示例:
SELECT CAST('123' AS UNSIGNED);
SELECT CAST('2026-08-09' AS DATE);
SELECT CAST(amount AS CHAR)
FROM payment;
MySQL 常用目标类型包括:
CHAR
DATE
DATETIME
DECIMAL
SIGNED
UNSIGNED
BINARY
JSON
具体可用类型和转换规则具有 MySQL 方言细节。
CONVERT()标记:[MySQL]
类型转换:
CONVERT(expression, type)
字符集转换:
CONVERT(expression USING character_set)
示例:
SELECT CONVERT('123', UNSIGNED);
SELECT CONVERT(column_name USING utf8mb4)
FROM some_table;
FORMAT():格式化数字显示标记:[MySQL]
FORMAT(number, decimal_places [, locale])
示例:
SELECT FORMAT(1234567.89, 2); -- '1,234,567.89'
返回的是字符串,适合报表显示,不适合继续参与数值计算。API 的本地化显示通常更适合放在前端或应用层。
MySQL 的 JSON 函数和路径语法具有明显方言特征,本节统一标记为 [MySQL]。
JSON_OBJECT()、JSON_ARRAY():构造 JSONJSON_OBJECT(key, value [, key, value] ...)
JSON_ARRAY(value1, value2, ...)
示例:
SELECT JSON_OBJECT(
'id', actor_id,
'name', CONCAT(first_name, ' ', last_name)
) AS actor_json
FROM actor;
SELECT JSON_ARRAY('mysql', 'nodejs', 'typescript');
JSON_EXTRACT() 与 ->JSON_EXTRACT(json_document, path [, path] ...)
json_column -> path
示例:
SELECT JSON_EXTRACT(profile, '$.address.city')
FROM customer_profile;
简写:
SELECT profile -> '$.address.city'
FROM customer_profile;
返回值仍是 JSON 值,字符串可能带引号。
JSON_UNQUOTE() 与 ->>JSON_UNQUOTE(json_value)
json_column ->> path
示例:
SELECT profile ->> '$.address.city' AS city
FROM customer_profile;
->> 可以理解为提取并取消 JSON 字符串引号。
JSON_SET()、JSON_INSERT()、JSON_REPLACE()JSON_SET(document, path, value [, path, value] ...)
JSON_INSERT(document, path, value [, path, value] ...)
JSON_REPLACE(document, path, value [, path, value] ...)
区别:
| 函数 | 路径不存在 | 路径已存在 |
|---|---|---|
JSON_SET |
新增 | 替换 |
JSON_INSERT |
新增 | 保留原值 |
JSON_REPLACE |
不新增 | 替换 |
示例:
UPDATE customer_profile
SET profile = JSON_SET(profile, '$.theme', 'dark')
WHERE customer_id = 1;
JSON_REMOVE()JSON_REMOVE(document, path [, path] ...)
示例:
UPDATE customer_profile
SET profile = JSON_REMOVE(profile, '$.temporaryToken')
WHERE customer_id = 1;
JSON_CONTAINS()JSON_CONTAINS(document, candidate [, path])
示例:
SELECT JSON_CONTAINS(
'["mysql", "nodejs"]',
'"mysql"'
);
注意 candidate 本身也必须是合法 JSON 文本。
JSON_ARRAYAGG(expression)
JSON_OBJECTAGG(key, value)
示例:
SELECT JSON_ARRAYAGG(title)
FROM film
WHERE rating = 'G';
JSON 聚合适合生成 API 或报表结构,但大结果会增加 MySQL 的内存、网络和 JSON 解析成本。复杂 API 通常仍应评估由应用层组装是否更清晰。
| 函数 | 用途 |
|---|---|
JSON_VALID(value) |
判断是否为合法 JSON |
JSON_TYPE(value) |
返回 JSON 值类型 |
JSON_LENGTH(value [, path]) |
返回数组长度或对象成员数量 |
JSON_KEYS(object [, path]) |
返回对象键数组 |
JSON_MERGE_PATCH(doc1, doc2, ...) |
按 merge-patch 规则合并 |
JSON_TABLE() |
把 JSON 映射成关系表,功能强但语法较复杂 |
窗口函数从 MySQL 8.0 开始成为日常报表查询的重要能力。下面的函数属于标准 SQL 窗口函数,标记为 [SQL]。
窗口函数不会像 GROUP BY 一样把多行压缩成一行,而是在保留明细行的同时计算排名、累计值或相邻行。
通用语法:
window_function(...) OVER (
[PARTITION BY expression]
[ORDER BY expression]
[frame_clause]
)
ROW_NUMBER():连续行号ROW_NUMBER() OVER (
[PARTITION BY expression]
ORDER BY expression
)
给每个客户的付款按时间编号:
SELECT
payment_id,
customer_id,
payment_date,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY payment_date, payment_id
) AS payment_number
FROM payment;
RANK() 与 DENSE_RANK()RANK() OVER (ORDER BY expression)
DENSE_RANK() OVER (ORDER BY expression)
区别:假设分数为 100、90、90、80:
| 分数 | RANK() |
DENSE_RANK() |
|---|---|---|
| 100 | 1 | 1 |
| 90 | 2 | 2 |
| 90 | 2 | 2 |
| 80 | 4 | 3 |
LAG()、LEAD():读取前后行LAG(expression [, offset [, default]]) OVER (...)
LEAD(expression [, offset [, default]]) OVER (...)
比较本次付款和上次付款:
SELECT
customer_id,
payment_date,
amount,
LAG(amount) OVER (
PARTITION BY customer_id
ORDER BY payment_date, payment_id
) AS previous_amount
FROM payment;
SUM、AVG、COUNT、MIN、MAX 可以配合 OVER() 使用。
累计付款金额:
SELECT
payment_id,
customer_id,
payment_date,
amount,
SUM(amount) OVER (
PARTITION BY customer_id
ORDER BY payment_date, payment_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM payment;
经验:窗口函数的 ORDER BY 应尽量具有稳定顺序。如果时间可能相同,再加主键作为第二排序条件。
| 函数 | 用途 |
|---|---|
NTILE(n) |
把结果分成 n 组 |
FIRST_VALUE(value) |
窗口中的第一个值 |
LAST_VALUE(value) |
窗口中的最后一个值,特别注意窗口 frame |
NTH_VALUE(value, n) |
窗口中的第 n 个值 |
CUME_DIST() |
累积分布 |
PERCENT_RANK() |
百分比排名 |
以下函数属于 MySQL 8.0 正则表达式能力,统一标记为 [MySQL]。
REGEXP_LIKE(string, pattern [, match_type])
REGEXP_INSTR(string, pattern [, ...])
REGEXP_REPLACE(string, pattern, replacement [, ...])
REGEXP_SUBSTR(string, pattern [, ...])
示例:
SELECT REGEXP_LIKE('user@example.com', '^[^@]+@[^@]+$');
SELECT REGEXP_REPLACE('abc-123', '[0-9]+', 'NUMBER');
正则适合清洗、分析和管理脚本,但不要把复杂接口校验全部推给数据库。正则匹配也通常难以利用普通 B-tree 索引。
本节同时包含 SQL 标准身份函数和 MySQL 方言函数,具体以每一项的标记为准。
标记:[MySQL]
DATABASE()
SCHEMA()
SELECT DATABASE();
CURRENT_USER、SESSION_USER、SYSTEM_USER 是标准 SQL 身份概念,标记为 [SQL];MySQL 同时提供带括号的函数形式,并提供 USER(),后者标记为 [MySQL]。
USER()
CURRENT_USER()
SESSION_USER()
SYSTEM_USER()
USER() 更接近客户端连接身份,CURRENT_USER() 表示 MySQL 权限检查实际使用的账号,两者不一定相同。
标记:[MySQL]
VERSION()
标记:[MySQL]
CONNECTION_ID()
排查锁等待和连接问题时可能用到。
标记:[MySQL]
LAST_INSERT_ID()
它是当前连接范围内的状态。使用连接池时,不要先用一个 pool.query() 插入,再用另一个 pool.query() 调用 LAST_INSERT_ID(),因为两次查询可能使用不同连接。
Node.js 使用 mysql2 时,应优先读取写入结果:
const [result] = await pool.execute<ResultSetHeader>(
"INSERT INTO actor (first_name, last_name) VALUES (?, ?)",
["MARY", "SMITH"],
);
console.log(result.insertId);
以下函数标记为 [MySQL]。
| 函数 | 用途 |
|---|---|
MD5(string) |
MD5 摘要,不适合密码安全 |
SHA1(string) |
SHA-1 摘要,不适合密码安全 |
SHA2(string, bits) |
SHA-2 摘要,如 256、512 |
AES_ENCRYPT(value, key) |
AES 加密,密钥管理和模式配置需要谨慎 |
AES_DECRYPT(value, key) |
AES 解密 |
TO_BASE64(value) |
Base64 编码,不是加密 |
FROM_BASE64(value) |
Base64 解码 |
不要使用 MD5()、SHA1() 或单次 SHA2() 保存用户密码。密码应在 Node.js 应用层使用专门的密码哈希算法,例如 Argon2、bcrypt 或 scrypt,并正确设置成本参数和随机盐。
标记:[MySQL]
BIT_COUNT(number)
聚合位函数:
BIT_AND(expression)
BIT_OR(expression)
BIT_XOR(expression)
位标记可节省空间,但可读性和可维护性较差,普通业务状态更适合明确字段或关联表。
标记:[MySQL]
UUID()
UUID_TO_BIN(uuid [, swap_flag])
BIN_TO_UUID(binary_uuid [, swap_flag])
UUID() 返回文本 UUID。高写入量表如果使用随机 UUID 作为聚簇主键,可能影响索引局部性;需要结合主键设计、存储格式和业务分布式需求评估。
标记:[MySQL]
MATCH(column1, column2, ...)
AGAINST(search_string [search_modifier])
示例:
SELECT film_id, title
FROM film_text
WHERE MATCH(title, description)
AGAINST('database' IN NATURAL LANGUAGE MODE);
需要 FULLTEXT 索引。复杂搜索业务通常还要评估 Elasticsearch、OpenSearch 等专用搜索系统。
标记:[MySQL]
常见函数:
ST_GeomFromText()
ST_AsText()
ST_Distance()
ST_Contains()
ST_Within()
只有涉及地图、坐标和空间索引时才需要深入学习。
对索引列套函数可能导致 MySQL 无法直接使用普通索引完成范围查找。
不推荐:
SELECT *
FROM payment
WHERE DATE(payment_date) = '2026-08-09';
推荐:
SELECT *
FROM payment
WHERE payment_date >= '2026-08-09 00:00:00'
AND payment_date < '2026-08-10 00:00:00';
不推荐:
WHERE LOWER(email) = 'user@example.com'
更合理的方案可能是:
最终需要使用:
EXPLAIN SELECT ...;
检查真实执行计划,而不是看到函数就断言“一定不走索引”。
WHERE 与 HAVING 中使用函数WHERE 在分组和聚合之前过滤行:
SELECT customer_id, SUM(amount) AS total_amount
FROM payment
WHERE payment_date >= '2005-01-01'
GROUP BY customer_id;
HAVING 在分组之后过滤聚合结果:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM payment
GROUP BY customer_id
HAVING SUM(amount) >= 100;
能够在 WHERE 中过滤的原始数据不要全部推迟到 HAVING,尽早减少参与聚合的行通常更高效。
NULL 传播规则SQL 中 NULL 表示未知或缺失,不等于空字符串和数字零。
错误:
WHERE return_date = NULL
正确:
WHERE return_date IS NULL
WHERE return_date IS NOT NULL
多数普通函数接收到 NULL 时会返回 NULL,但并非全部函数都如此。例如:
CONCAT('a', NULL) 返回 NULL;CONCAT_WS(',', 'a', NULL, 'b') 会跳过 NULL;COUNT(column) 忽略 NULL;COUNT(*) 统计行,不忽略包含 NULL 的行;COALESCE() 就是专门用于选择非 NULL 值。编写查询时应主动确认函数的 NULL 行为。
| 目的 | 优先记忆 | 标记 |
|---|---|---|
| 统计行数 | COUNT(*) |
[SQL] |
| 求和/平均 | SUM()、AVG() |
[SQL] |
| 最大/最小 | MAX()、MIN() |
[SQL] |
| 字符串拼接 | CONCAT()、CONCAT_WS() |
[MySQL] |
| 字符数/字节数 | CHAR_LENGTH() / LENGTH() |
[SQL] / [MySQL] |
| 去两端空格 | TRIM() |
[SQL] |
| 截取字符串 | SUBSTRING() |
[SQL] |
| 当前时间 | CURRENT_TIMESTAMP / NOW() |
[SQL] / [MySQL] |
| 日期加减 | DATE_ADD()、DATE_SUB() |
[MySQL] |
| 日期差 | DATEDIFF()、TIMESTAMPDIFF() |
[MySQL] |
| 时间格式化 | DATE_FORMAT() |
[MySQL] |
| 四舍五入 | ROUND() |
[SQL] |
| 空值替代 | COALESCE() / IFNULL() |
[SQL] / [MySQL] |
| 条件判断 | CASE / IF() |
[SQL] / [MySQL] |
| 类型转换 | CAST() / CONVERT() |
[SQL] / [MySQL] |
| JSON 取值 | JSON_EXTRACT()、->> |
[MySQL] |
| 排名 | ROW_NUMBER()、RANK() |
[SQL] |
| 前后行 | LAG()、LEAD() |
[SQL] |
学习函数时不要只背名字,至少同时记住四件事:
NULL 时的行为;推荐先熟练掌握:
COUNT / SUM / AVG / MIN / MAX
CONCAT / CHAR_LENGTH / SUBSTRING / TRIM
CURRENT_TIMESTAMP / DATE_ADD / DATEDIFF / TIMESTAMPDIFF
ROUND / CEIL / FLOOR
COALESCE / NULLIF / CASE
CAST
ROW_NUMBER / LAG
然后根据实际项目需要再学习 JSON、正则、空间、全文搜索和加密函数。