SQL 速查
查询语句、连接与子查询、聚合与窗口函数、常用函数、数据变更与事务、执行计划与索引:SQL 语言全场景速查。
查询基础
SELECT/WHERE/ORDER/LIMIT 与条件表达式
SELECT 基础查询热门
从表里取数据的基本写法:选择列、指定来源表,再叠加 WHERE / ORDER BY / LIMIT;显式列清单比 SELECT * 更利于稳定输出与减少无关列读取。
WHERE 条件过滤热门
在分组与排序之前过滤行:WHERE 只保留条件为 TRUE 的行;结合 IS NULL、范围与集合条件可以精确圈定数据。
LIMIT 分页与 Top N热门
限定返回行数与偏移:LIMIT n 取前 n 行,OFFSET 跳过若干行;深分页性能差,生产上用键集分页替代大 OFFSET。
IS NULL / IS NOT NULL 判空
NULL 表示“未知”,不能用等号判断;IS [NOT] NULL 是可靠的判空方式,也是计数与条件过滤中常见的坑位。
ORDER BY 排序与稳定性
对结果排序:支持多列、表达式、列序号与别名;没有 ORDER BY 时返回顺序不保证,分页与 Top N 场景必须显式排序。
LIKE 模糊匹配与转义
百分号匹配任意长度、下划线匹配单个字符;前缀模式可走索引,前后都加百分号基本等于全表扫描,必要时用 ESCAPE 处理字面量。
NOT IN 的 NULL 陷阱
NOT IN 对包含 NULL 的集合几乎总是返回空结果:因为 x <> NULL 是 UNKNOWN。识别与改写它是排查“查询莫名没数据”的高频问题。
SELECT 列选择与别名 AS
用别名给输出列和表起短名字:AS 可省略但显式更清楚;别名只影响结果集与部分子句,不改变原表结构。
DISTINCT 去重
对结果集去重:作用于整个 SELECT 列清单,不是单列;统计去重计数用 COUNT(DISTINCT col),注意 NULL 不参与计数。
IN 列表条件
IN 命中一组离散值,等价于若干 OR 但更简洁;列表很长时优化器代价上升,NOT IN 遇到 NULL 是经典陷阱。
CASE 表达式(简单与搜索形式)
SQL 中的条件分支:简单 CASE 做等值匹配,搜索 CASE 书写任意条件;分支自上而下取首个命中,缺 ELSE 时返回 NULL。
SELECT 计算列与表达式
在 SELECT 里直接做算术得到派生列:支持括号、四则运算与取模;注意整数除法与除零行为在 MySQL、PostgreSQL 间不同。
BETWEEN 范围条件
BETWEEN a AND b 等价于 >= a AND <= b,两端都包含;对时间列用日期做边界时要留意时间部分,推荐半开区间写法。
NULL 三值逻辑与运算表现
NULL 参与比较得到 UNKNOWN、参与算术得到 NULL、参与字符串拼接通常也得到 NULL;理解这些规则才能写对过滤与统计。
UNION 与 UNION ALL 合并结果集
纵向拼接两个查询结果:UNION 去重、UNION ALL 保留全部行;能确定无重复时优先 UNION ALL,省去去重开销。
AND / OR / NOT 逻辑组合
组合多个条件时先搞清优先级:NOT 高于 AND、AND 高于 OR;SQL 不保证短路求值,可能报错的表达式不要靠条件顺序保护。
比较运算符与排序规则影响
=、<>、>、< 等比较运算的写法与两个高频细节:字符串比较受排序规则影响(大小写敏感性);与 NULL 比较需要专门语法。
SQL 字面量写法:字符串、数字、日期时间
把常量写进 SQL 的规范写法:字符串用单引号、日期时间用带类型前缀的标准字面量,避免依赖各数据库的松式写法。
SELECT 无来源查询(常量与函数验证)
不指定表也能求值:验证函数、时区、隐式转换行为时很顺手;MySQL 兼容 FROM DUAL,PostgreSQL 不需要 FROM。
别名在 WHERE / HAVING 中的可用性
SELECT 里定义的列别名不能用在 WHERE:逻辑执行顺序中 WHERE 在 SELECT 之前;需要过滤派生列时写原表达式或套子查询。
CASE 自定义排序顺序
按业务枚举顺序排序(如 紧急 > 高 > 普通)的常用写法:把状态映射成数字参与 ORDER BY,避免按拼音或字母排序。
标识符引用:反引号与双引号
表名、列名与保留字冲突时的引用方式:MySQL 用反引号,PostgreSQL 用双引号且区分大小写;跨库 SQL 不要混用。
REGEXP 正则匹配
按正则表达式匹配字符串:MySQL 用 REGEXP/RLIKE 运算符与 REGEXP_* 函数族,PostgreSQL 用 ~ 系列运算符,大小写规则各自不同。
排序中的 NULL 位置处理
NULL 在排序中的位置两库默认不同:MySQL 把 NULL 视为极小值(升序在前),PostgreSQL 视为极大值(升序在后),且只有 PG 支持 NULLS FIRST/LAST。
字符串转义与反斜杠差异
字符串里嵌引号、百分号的处理方式;MySQL 默认把反斜杠当转义符,PostgreSQL 标准字符串中反斜杠是普通字符,迁移前要核对。
布尔表达式与真值类型
PostgreSQL 有原生 boolean 类型,MySQL 用 tinyint(1) 与 0/1 近似;两库在 WHERE 中直接使用布尔表达式的宽严不同。
多列去重与 PostgreSQL DISTINCT ON
多列 DISTINCT 按组合去重;PostgreSQL 的 DISTINCT ON 能实现“每组保留一行”,MySQL 需要用窗口函数改写。
NULL 安全比较:<=> 与 IS DISTINCT FROM
普通等号遇到 NULL 得 UNKNOWN,需要 NULL 安全相等时:MySQL 用 <=>,标准与 PostgreSQL 用 IS [NOT] DISTINCT FROM。
集合运算中的 ORDER BY 与 LIMIT
ORDER BY/LIMIT 写在最后时作用于整个合并结果;要给单个分支排序或限行,必须用括号包住该分支。
INTERSECT 与 EXCEPT 交集/差集
标准集合运算:INTERSECT 取两查询都有的行,EXCEPT 取前一查询有而后一查询没有的行,默认都去重。
SQL 注释:-- 与 /* */
三种注释写法与可执行注释的区别:MySQL 支持 # 单行注释与版本注释 /*! */,PostgreSQL 遵循标准的两类注释。
行构造器比较 (a, b) IN ((…),(…))
把多个列当作一个元组参与 IN 或等值比较,表达“多列组合命中多个候选项”比 OR 链更短更清晰。
查询结果导出:INTO OUTFILE 与 COPY
把 SELECT 结果写到服务器文件:MySQL 用 INTO OUTFILE(受 secure_file_priv 限制),PostgreSQL 用 COPY(服务端)或 psql \copy(客户端)。
连接与子查询
各类 JOIN、子查询与 CTE 写法
LEFT JOIN 左外连接热门
保留左表全部行,右表没有匹配时用 NULL 补齐;适合“主表 + 可选关联信息”的查询。
INNER JOIN 内连接基础热门
按连接条件把两张表的行配对,只保留匹配成功的组合;显式 JOIN 语法比逗号连接更清晰,也不容易漏写连接条件。
ON 与 WHERE 在外连接中的区别
内连接里 ON 与 WHERE 可以互换;外连接中不能:ON 决定配对,WHERE 在配对结果上过滤,写在 WHERE 会滤掉补 NULL 的行。
反连接:找出不存在于另一表的行
反连接三种写法:NOT EXISTS、NOT IN、LEFT JOIN IS NULL;语义与 NULL 行为各不相同,优先 NOT EXISTS。
一对多连接的行数放大
连接“多”侧后结果按多行展开:订单连明细后每个订单出现多行,做统计前先确定口径。
NOT EXISTS 反连接
NOT EXISTS 找“没有对应匹配”的行:与 NULL 无关,是反连接的推荐写法;配合内表索引效率稳定。
CTE:WITH 公共表表达式
用 WITH 给子查询起名字,把多层查询拆成可读的步骤;MySQL 8.0+ 与 PostgreSQL 均支持,作用域限于紧随的语句。
EXISTS 判断存在性
EXISTS 判断子查询是否有行:逻辑上找到首行即返回真,适合“是否存在”类过滤。
递归 CTE:树形结构展开
WITH RECURSIVE 以“初始行 + 递归项”逐步展开层级数据,典型场景是组织架构、分类树、物料清单。
三表及多表连接
多个表依次连接:链式书写、逐步列出条件;每步都可能放大或缩小行数,最终结果粒度要提前想清楚。
每组取最新一行:窗口函数写法
“每个用户最近一笔订单”这类需求的通用解法:ROW_NUMBER 按组内排序编号,取 rn=1 后左连接到主表。
自连接:表连接自身
同一张表连接自身:员工与经理、成对比较等场景;必须用两个不同别名区分两侧,否则无法引用。
左连接后的计数口径
左连接统计时 COUNT(*) 统计连接后的行数(含补 NULL 行),COUNT(右表列) 只统计真正匹配上的行。
连接键为 NULL 的行不会匹配
NULL 与其他任何值(包括 NULL)比较都是 UNKNOWN:连接键含 NULL 的行在内连接中直接消失,在左连接中保留但补 NULL。
IN 子查询与半连接
IN 子查询表达“存在于另一张表”:只要找到匹配就返回该行,不放大行数;优化器通常把它改写为半连接。
派生表:FROM 里的子查询
把子查询当作临时表参与连接或过滤,必须给别名;MySQL 8.0.22 起支持下推条件到派生表,旧版本可能先物化整表。
子查询与 JOIN 的选择
同一需求可以写成 IN/EXISTS 或 JOIN:核心差异在“是否放大行数”与“是否输出另一表列”,性能以执行计划实测为准。
判断存在性:EXISTS 优于 COUNT
只需要判断“有没有”时,EXISTS 找到首行即停;COUNT(*) 要把所有匹配行数完,代价更高。
CROSS JOIN 笛卡尔积
两表无条件配对得到所有组合:常用于生成“尺码 × 颜色”这类组合集,误用时行数按乘积增长。
先聚合再连接
先在子查询里过滤并汇总,得到小结果集再连接主表,通常比连接全部明细再聚合更省资源。
子查询中 ORDER BY 与 LIMIT 的语义
子查询里“排序取前 N 行”只有搭配 LIMIT 才确定;不带 LIMIT 的 ORDER BY 在优化层可能被忽略,不能依赖它。
SELECT 列表里的子查询
在结果列里内嵌标量子查询得到附加信息(计数、最新值):可读性好,但逻辑上逐行执行,行数大时优先改 JOIN 或窗口函数。
LATERAL:子查询引用左侧表
让 FROM 中的子查询引用同一 FROM 里写在它左侧的表(类似逐行相关子查询):PostgreSQL 9.3+、MySQL 8.0.14+ 支持。
相关子查询
子查询引用外层查询的列,逻辑上逐行求值;配合内表索引可高效,否则每行一次扫描。
RIGHT JOIN 右外连接
保留右表全部行,左表无匹配补 NULL;可读性不如把两表交换后用 LEFT JOIN,团队规范里常要求统一成 LEFT JOIN。
标量子查询
用于取值场景的子查询:必须至多返回一行一列;没有匹配行时结果为 NULL。
多个 CTE 与互相引用
一条 WITH 里可以定义多个 CTE(逗号分隔);后定义的可引用先定义的,形成流水线式查询结构,不允许循环引用。
递归 CTE 的深度限制与环检测
数据成环或条件写错时递归不终止:MySQL 有 cte_max_recursion_depth 保护(默认 1000),PostgreSQL 需要自行加深度或路径防护。
CTE 配合 UPDATE / DELETE / INSERT
把筛选逻辑写成 CTE 再驱动数据变更:MySQL 允许 WITH 引导 UPDATE/DELETE;PostgreSQL 还允许在 CTE 内直接执行带 RETURNING 的数据修改。
FULL OUTER JOIN 全外连接
两边都保留、互不匹配的位置补 NULL:PostgreSQL 与标准 SQL 支持 FULL OUTER JOIN,MySQL 不支持,可用 LEFT/RIGHT 加 UNION 模拟。
CTE、派生表与临时表怎么选
单语句内拆解逻辑用 CTE;同一中间结果被多处引用、需要索引或跨语句复用时用临时表。
分表结果合并统计
按时间或业务分表后,用 UNION ALL 合并各分片再聚合;列结构必须一致,聚合放在合并之后。
CTE 物化与内联的选择
CTE 是内联展开还是物化成临时结果会影响性能:PostgreSQL 12 起默认按引用情况决定,MySQL 由优化器自行决定。
展开 JSON 数组为多行(JSON_TABLE 与 LATERAL)
把一行里的 JSON 数组拆成多行参与过滤与统计:MySQL 8 用 JSON_TABLE,PostgreSQL 用 jsonb_array_elements 配合 LATERAL。
USING 与 NATURAL JOIN
两表同名列可用 USING(col) 简化连接条件,结果中该列只出现一次;NATURAL JOIN 按所有同名列自动连接,隐式且风险高。
递归 CTE 生成数字与日期序列
用递归生成连续整数或日期,补全报表空缺天数;PostgreSQL 另有 generate_series,写法更短。
ANY / ALL 子查询比较
比较运算符配合 ANY/SOME/ALL 表达“与任意一个比较 / 与所有值比较”:= ANY 等价 IN,<> ALL 等价 NOT IN,NULL 规则相同。
非等值连接:范围与不等条件
连接条件可以用任何布尔表达式:价格落在区间、时间重叠等场景用范围连接,注意区间重叠会导致一行命中多条规则。
复合键连接
多列共同构成连接键时,ON 中逐列相等;漏写某一列会放宽条件导致错配或行数翻倍,复合索引也要按列序匹配。
强制连接顺序:STRAIGHT_JOIN 与提示
MySQL 可用 STRAIGHT_JOIN 强制按书写顺序连接,用于优化器偶发选错顺序的场景;PostgreSQL 一般通过统计信息与规划参数间接影响。
聚合与窗口函数
分组统计、排名与累计计算
GROUP BY 分组统计热门
按列值分组后对每组计算聚合:SELECT 列表里只能出现分组列与聚合表达式,这是标准与两库默认强制的约束。
COUNT 计数热门
统计行数或非空值个数:COUNT(*) 统计行数,COUNT(col) 只统计该列非 NULL 的值;无匹配行时返回 0。
窗口函数概念与语法
窗口函数在保留明细行的同时做组内计算:函数 OVER (PARTITION BY … ORDER BY … [帧]);MySQL 8.0+ 与 PostgreSQL 均支持。
HAVING 过滤分组结果
HAVING 在分组与聚合之后过滤:条件里可以用聚合函数,HAVING COUNT(*) > 5 是典型用法。
ROW_NUMBER 组内行号
为分区内的行按排序生成连续唯一编号(1,2,3…),用于分组取前 N 行、去重取最新等场景。
COUNT(DISTINCT) 去重计数
统计去重后的个数,例如独立用户数;NULL 不参与计数,MySQL 还支持多列组合去重。
SUM 求和
对数值列求和,忽略 NULL;没有任何行或全为 NULL 时返回 NULL,报表场景常用 COALESCE 兜底。
WHERE 与 HAVING 的分工
WHERE 过滤行、HAVING 过滤组:同一条件写在哪一层会影响参与分组的数据量与结果正确性。
累计求和(running total)
用 SUM(...) OVER (ORDER BY …) 计算截至当前行的累计值:销售额累计、账户流水余额等经典场景。
COUNT(*) 与 COUNT(col) 的区别
两者都可能出现在同一条 SQL 里但含义不同:COUNT(*) 数行;COUNT(col) 数非 NULL 值。排查数据质量时常用两者差值找空值。
按天/月聚合统计
时间维度的报表基础:把时间戳截断到天或月再分组;注意索引友好写法与跨天边界口径。
每组取前 N 行
分组 Top N 的标准解法:ROW_NUMBER(严格 N 行)或 RANK(含并列)编号后在外层过滤,窗口函数不能直接写进 WHERE。
按表达式或函数分组
分组键可以是表达式:按天/月统计是常见需求;表达式分组通常无法直接用普通索引,可考虑生成列或预聚合。
条件聚合(多种口径一次算出)
用 CASE/布尔表达式在聚合内部区分条件,一条 SQL 同时算出多个指标,避免多次扫描同一张表。
LAG 取上一行值
取当前行之前第 N 行的值:环比、差值、会话切分等时序计算的基础;首行没有前值时返回默认值(可指定)。
窗口函数去重:每组只留一行
去重取最新/最早一条的通用写法:PARTITION BY 去重键 + ORDER BY 排序 + rn=1;比 GROUP BY 更容易保留整行信息。
AVG 平均值与口径
平均值 = 非 NULL 值之和 ÷ 非 NULL 值个数;与 SUM/COUNT(*) 的口径不同,报表口径要先定义清楚。
多列分组
GROUP BY 多列时按列组合分组:结果行数等于组合数;顺序影响中间排序但结果集一致。
GROUP_CONCAT 分组拼接(MySQL)
把组内值拼成一个字符串:支持 DISTINCT、排序与自定义分隔符;默认长度上限 1024 字节,超出会静默截断。
RANK 排名(并列跳号)
按排序值排名:并列名次相同,后续名次跳号(1,1,3…);取“前 N 名含并列”用 RANK 而不是 ROW_NUMBER。
窗口函数与 GROUP BY 的选择
需要保留明细行、做组内对比时用窗口函数;只需要每组的汇总值时用 GROUP BY 更直接。
ONLY_FULL_GROUP_BY 与分组合法性
非聚合列不在 GROUP BY 中时:MySQL 默认模式直接报错(ONLY_FULL_GROUP_BY),PostgreSQL 同样拒绝;识别主键函数依赖可放行。
聚合函数与 NULL 的关系
SUM/AVG/MAX/MIN 忽略 NULL 输入;无输入时返回 NULL;COUNT 家族的行为不同(COUNT(*) 为 0)。写报表时这组规则必须分清。
占比与比率计算
比率 = 部分 / 整体:用聚合相除并防止整数除法与除零;MySQL 与 PostgreSQL 的整数除法行为不同。
环比计算(与上期对比)
用 LAG 取上一期值算环比:注意分区(门店/品类)与排序(月份)要写全,否则跨对象对比会出错。
MIN / MAX 最值统计
取列的极小值与极大值;忽略 NULL,可用于时间范围、价格区间等场景,配合 GROUP BY 得到分组极值。
用 SUM(CASE) 做行转列透视
不依赖专用函数实现行列转换:把要展开的维度值写成 CASE 分支,外层 SUM/COUNT 得到透视表;维度值需提前确定。
移动平均
用窗口帧计算近 N 行均值(如近 7 天平均销量),平滑波动;帧写法 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW。
占比计算(组内份额)
当前行值 ÷ 组内总和:用 SUM(...) OVER (PARTITION BY ...) 得到分母,一次扫描算出份额。
组内计数广播 COUNT(*) OVER
不改行数地给每行附上组内统计(组大小、组内极值),常用于占比、排名并列判断与数据探查。
ROLLUP 分组小计与合计
GROUP BY ... WITH ROLLUP 在结果中追加小计与总计行:MySQL 8 与 PostgreSQL 均支持,小计行的分组列显示为 NULL。
LAST_VALUE 与默认帧的坑
LAST_VALUE 默认帧是“分区起点到当前行”,直接写会得到当前行值;要取分区最后一行必须显式扩展帧到分区结尾。
窗口函数的使用限制
窗口函数只能出现在 SELECT 列表与 ORDER BY 中:WHERE、GROUP BY、HAVING 里不能用;要筛选计算结果必须套子查询。
DENSE_RANK 排名(并列不跳号)
并列名次相同且后续不跳号(1,1,2,3…):适合“等级档位”类排名展示。
LEAD 取下一行值
取当前行之后第 N 行的值:计算截止时间、下次登录间隔、流程步骤耗时等场景。
JSON 聚合:JSON_ARRAYAGG 与 json_agg
把组内数据聚合成 JSON 数组/对象,接口直接消费;MySQL 8 用 JSON_ARRAYAGG/JSON_OBJECTAGG,PostgreSQL 用 json_agg/jsonb_object_agg。
大表计数:精确与近似
InnoDB 的 COUNT(*) 需要扫描;报表里动辄全表计数时,可用统计信息近似值或计数表方案。
ROWS 与 RANGE 帧的区别
窗口帧决定函数看哪些行:ROWS 按物理行数,RANGE 按排序值的区间(并列值会一起进来);省略时默认 RANGE。
同比计算(跨 12 期)
LAG(x, 12) 取去年同月值算同比;前提是数据按月连续、每年 12 期,写死偏移量时先校验数据完整性。
带 FILTER 的聚合(PostgreSQL)
PostgreSQL 支持聚合函数加 FILTER (WHERE …),只对满足条件的输入计算;MySQL 需要用 CASE 表达式达到同样效果。
FIRST_VALUE 组内首值
取窗口帧内首行的值:把“组内最早/排在首位”的字段广播到每一行,用于对比与填充。
窗口函数的默认帧
有 ORDER BY 时默认帧是 RANGE UNBOUNDED PRECEDING AND CURRENT ROW;没有 ORDER BY 时是整个分区。这解释了很多“结果和想象不一样”。
HAVING 中使用别名(方言差异)
MySQL 允许在 HAVING 引用 SELECT 别名;PostgreSQL 不支持,需要重复写聚合表达式或套一层子查询。
string_agg 字符串聚合(PostgreSQL)
PostgreSQL 的字符串聚合函数:string_agg(值, 分隔符 [ORDER BY …]);MySQL 对应 GROUP_CONCAT。
DISTINCT 与 GROUP BY 去重的等价关系
SELECT DISTINCT a, b 与 SELECT a, b ... GROUP BY a, b 在结果层等价;需要附加聚合时用 GROUP BY 更自然。
NTILE 分桶
把分区内行按顺序尽量均匀分成 n 桶(1..n),用于分位、TOP 比例抽样等场景。
GROUPING() 区分小计行
GROUPING(列) 在超级聚合(小计/合计)行返回 1、普通行返回 0,用来把 NULL 与小计正确区分开。
WINDOW 子句命名窗口
多个窗口函数复用同一 OVER 定义时,用 WINDOW 子句命名,减少重复与笔误;MySQL 8 与 PostgreSQL 都支持。
中位数与百分位(percentile_cont)
中位数/百分位用分位函数计算:PostgreSQL 用 percentile_cont/percentile_disc;MySQL 8 没有内置等价函数,需要用窗口函数近似或应用层计算。
PERCENT_RANK 相对排名
相对排名 = (名次 - 1) / (总行数 - 1),取值 0 到 1;用于“排在前面百分之多少”。
GROUPING SETS 与 CUBE(PostgreSQL)
一条查询同时产出多种分组口径:PostgreSQL 支持 GROUPING SETS 与 CUBE;MySQL 8 只有 ROLLUP。
GROUP BY 列序号
MySQL 与 PostgreSQL 都允许 GROUP BY 1 这类列序号写法:简短但可读性差,改 SELECT 顺序时容易改错。
NTH_VALUE 取第 N 行值
取窗口帧内第 N 行的值:例如组内第二早的订单时间;N 超出帧范围时返回 NULL。
CUME_DIST 累积分布
返回“小于等于当前排序值的行数占分区比例”,取值 (0,1];与 PERCENT_RANK 的区别在于并列值的处理。
函数速查
字符串、日期时间、数值与 JSON 函数
CONCAT 字符串拼接热门
拼接多个字符串:MySQL 的 CONCAT 遇到 NULL 会让整行结果变 NULL,带分隔符拼接用 CONCAT_WS 会跳过 NULL 项。
COALESCE 取首个非 NULL 值
按顺序返回首个非 NULL 的参数:做默认值、多字段兜底、把 NULL 转成业务默认值时使用,是 SQL 标准函数。
NOW / CURRENT_TIMESTAMP 当前时间
取当前日期时间:MySQL NOW() 在语句执行时取值;PostgreSQL now() 等于事务开始时间,同一事务内多次调用结果相同。
DATE_ADD / INTERVAL 日期加减
日期加减的主力语法:MySQL 用 DATE_ADD(d, INTERVAL n UNIT) 或 d ± INTERVAL;PostgreSQL 用 d ± interval 'n unit'。
SUBSTRING 截取子串
按位置截取子串:MySQL 支持负数位置从尾部数;PostgreSQL 用 substring(str from start for len),位置从 1 开始。
LIKE 通配符与 ESCAPE 转义
LIKE 里 % 与 _ 是通配符:搜索字面量 % / _ 必须转义;用 ESCAPE 子句显式指定转义符比依赖反斜杠更清晰。
DATE_FORMAT 与 to_char 格式化
把日期时间格式化为字符串:MySQL 用 % 占位符,PostgreSQL 用 to_char 模板;两者模板字母含义不同,迁移时逐项核对。
date_trunc 时间截断(PostgreSQL)与 MySQL 等价写法
把时间截断到指定粒度(月/周/日/小时),做时间分桶统计的核心工具:PostgreSQL 有 date_trunc,MySQL 需要用日期运算拼装。
DECIMAL 精度与浮点误区
精确小数用 DECIMAL/NUMERIC:金额、数量、税率一律用定点类型;FLOAT/DOUBLE 是近似值,二进制表示会带来比较与累计误差。
JSON_EXTRACT 提取 JSON 值
从 JSON 文档里按键或下标取值:MySQL 有 JSON_EXTRACT 函数与 -> / ->> 运算符;PostgreSQL 用 jsonb 运算符或 jsonpath。
TRIM / LTRIM / RTRIM 去空白
去掉字符串两端的空白或指定字符:TRIM 默认只去半角空格,清洗用户输入时通常还要处理制表符与不换行空格。
REGEXP_REPLACE 正则替换
用正则做替换:批量清洗手机号、脱敏、格式规整;两库的默认替换次数与捕获组写法不同,跨库改写要逐条核对。
UUID 生成与存储
生成全局唯一标识:MySQL UUID() 返回 UUID v1 字符串;PostgreSQL 用 gen_random_uuid() 返回原生 uuid 类型。
EXTRACT 提取日期字段
从日期时间中取出年/月/日/时/分/周等字段:标准写法 EXTRACT(unit FROM expr),MySQL 另有 YEAR()/MONTH() 等简写。
UNIX_TIMESTAMP 与 FROM_UNIXTIME
Unix 时间戳与日期时间互转:MySQL 提供专用函数;PostgreSQL 用 extract(epoch) 与 to_timestamp,注意秒/毫秒与显示时区。
日期范围查询的边界写法
按时间过滤推荐半开区间 [起点, 次日):用 < 下月 1 日 而不是 BETWEEN ... 月末 23:59:59,避免漏掉毫秒数据。
CAST / CONVERT 类型转换
显式类型转换:MySQL 用 CAST(x AS SIGNED/UNSIGNED/CHAR/DECIMAL...),PostgreSQL 用 CAST(x AS type) 或 x::type;非法文本的处理策略不同。
-> 与 ->> 的区别与比较场景
JSON 取值的两个运算符:-> 返回 JSON 类型(字符串带引号、可用于后续 JSON 函数),->> 返回文本(用于比较与展示)。
LENGTH 与 CHAR_LENGTH 长度函数
计算字符串长度:MySQL 的 LENGTH 按字节、CHAR_LENGTH 按字符;PostgreSQL 的 length 按字符、octet_length 按字节,统计中文时容易踩坑。
哈希函数:MD5 / SHA2 / CRC32
计算摘要用于校验与去重:MySQL 提供 MD5/SHA1/SHA2/CRC32;PostgreSQL 内置 md5 与 sha 系列,更多算法用 pgcrypto 的 digest()。
NULLIF 等值转空
当两个参数相等时返回 NULL,否则返回首个参数:保护除法、把哨兵值(如 0、'N/A')转成 NULL 的标准做法。
TIMESTAMPDIFF 时间间隔计算
按指定单位计算两个时间点的差值(单位边界数):MySQL 直接支持;PostgreSQL 用时间戳相减取 epoch 换算。
CEIL / FLOOR 向上向下取整
向正负方向取整:CEIL 向上(天花板)、FLOOR 向下(地板);分页总页数、资源规格向上取整等场景常用。
JSON_SET / JSON_INSERT / JSON_REPLACE 与 jsonb_set
修改 JSON 文档内字段:MySQL 三兄弟分工是“插或改 / 只插 / 只改”;PostgreSQL 用 jsonb_set 与删除运算符。
LEFT / RIGHT 取左右子串
从字符串左端或右端取固定长度:常用于取编号前缀、手机号后四位、年份前两位;按字符计算,适合处理多字节文本。
REPLACE 字符串替换
把出现的所有子串替换为新内容:字面量替换、区分大小写、不支持正则;批量脱敏、清理固定后缀时高频使用。
STR_TO_DATE 字符串转日期
把字符串按格式解析为日期/时间值:导入外部数据、清洗脏日期列时使用;解析失败 MySQL 返回 NULL 并告警,PostgreSQL 直接报错。
时间戳与时区:CONVERT_TZ 与 AT TIME ZONE
跨时区存取的两种主流做法:MySQL 用 CONVERT_TZ 显式换算(依赖时区表);PostgreSQL 用 AT TIME ZONE 配合 timestamptz。
ROUND 四舍五入与精度陷阱
按小数位舍入:整数位也可通过负的位数处理;浮点参数的舍入行为与十进制精度有关,金额场景应使用 DECIMAL。
整数除法与除法精度
除法结果类型是跨库高频差异点:MySQL 的 / 得小数、DIV 取整数;PostgreSQL 整数相除直接截断,需要显式转 numeric。
JSON_CONTAINS 与 jsonb 包含查询
判断 JSON 里是否包含某个值或结构:MySQL 用 JSON_CONTAINS/JSON_CONTAINS_PATH,PostgreSQL 用 jsonb 的 @> 与存在运算符。
LOCATE / INSTR / POSITION 子串定位
返回子串首次出现的位置(找不到返回 0 或 NULL 视函数而定):三个函数参数顺序不同,是常见的抄错点。
SUBSTRING_INDEX 与 split_part 按分隔符切分
按分隔符取第 N 段或某段之前/之后的内容:解析路径、IP、版本号、逗号串时很实用。
DATEDIFF 天数差
计算两个日期相差的天数:MySQL DATEDIFF(a,b)=a-b(只取日期部分);PostgreSQL 用 date - date 直接得整数,时间戳相减得 interval。
DAYOFWEEK / WEEKDAY 与 DOW 星期计算
取星期序号做工作日/周末判断:三套编号(MySQL DAYOFWEEK 周日=1、WEEKDAY 周一=0、PG DOW 周日=0)混用是经典 bug 来源。
GREATEST / LEAST 多值比较(NULL 规则不同)
在一组值里取较大者/较小者:多条候选取优场景常用;两库对 NULL 的处理完全相反,是迁移时的隐藏雷区。
JSON_OBJECT / JSON_ARRAY 构造 JSON
在 SQL 里构造 JSON 对象与数组:接口返回、宽表导出、事件打点时拼 JSON 结构。
PostgreSQL JSON 与 JSONB 选型
两种 JSON 类型的取舍:jsonb 是解析后的二进制格式,支持索引与高效包含查询;json 原样保存文本,只适合“原样还原”场景。
LPAD / RPAD 补位与截断
按指定长度左右补位(常用于编号前补零),长度小于原串时会从另一端截断;补位串可以多于 1 个字符。
UPPER / LOWER 大小写转换
转大写或小写:跨库迁移时要注意比较行为差异——MySQL 默认排序规则不区分大小写,PostgreSQL 默认区分。
REGEXP_SUBSTR 与 regexp_match 提取匹配片段
从文本中抽出符合模式的片段:解析日志行、提取 URL 参数、抓取编号;比 SUBSTRING 固定位置灵活得多。
CURDATE / CURTIME 取日期或时间部分
只取当前日期或当前时间:MySQL 用 CURDATE()/CURTIME();PostgreSQL 用 CURRENT_DATE / LOCALTIME,注意日期加减写法的差异。
INTERVAL 运算的月底与语义细节
INTERVAL 的日期运算有隐含规则:加月遇到目标月天数不足会截到月末;'1 month' 与 '30 days' 并不等价。
MOD 取模与分桶
取余数:分表路由、采样、奇偶判断常用;对负数取模的符号规则与除零行为两库不同,分桶公式要写防御性版本。
JSON 路径语法与 jsonpath
路径表达式写法:MySQL 用 '$' 开头的路径字符串;PostgreSQL 运算符用键/数组路径,PG 12+ 的 jsonpath 与 MySQL 风格接近。
JSON_TABLE 展开 JSON 数组(MySQL 8)
把 JSON 数组转成关系行:MySQL 的 JSON_TABLE 可在 FROM 中直接展开,便于与普通表连接和聚合。
CONVERT USING 与 convert_from 字符集转换
字符集转换与检测:MySQL 有 CONVERT(expr USING cs) 与 CHARSET()/COLLATION();PostgreSQL 用 convert_to/convert_from 处理字节与文本互转。
IFNULL 与 ISNULL(MySQL)
MySQL 专用:IFNULL(a,b) 两参兜底;ISNULL(x) 是函数形式判断空值。PostgreSQL 没有 IFNULL,统一写 COALESCE。
WEEK / YEARWEEK 周编号与 mode 参数
按周分组统计时的口径控制:MySQL 的 WEEK/YEARWEEK 有 mode 参数决定周起始与计算方式,PostgreSQL 固定 ISO 周。
POW / SQRT / LOG 幂与对数
幂、开方与对数:容量估算、增长率、评分加权会用到;注意两库 LOG 的默认底数不同。
RAND / random 随机数与采样
生成随机数:随机抽样、造测试数据、加盐场景常用;对超大表 ORDER BY RAND() 代价很高,PG 可用 TABLESAMPLE。
JSON_VALID 与 JSON_TYPE / jsonb_typeof
校验与识别 JSON 结构:把文本转换/入列前先验证,排查线上数据时看清每个节点的类型。
jsonb_array_elements 展开数组(PostgreSQL)
PostgreSQL 把 JSON 数组展开成多行的惯用函数族:可带文本版本与序号,是处理 JSON 明细的基础操作。
LAST_DAY 取月末日期
返回所在月份的最后一天:做月度对账、账单周期、月末快照时常用;MySQL 有 LAST_DAY,PostgreSQL 用月初加一个月再减一天。
JSON_MERGE_PATCH / MERGE_PRESERVE 与 || 合并
合并两个 JSON 文档:MySQL 提供 patch(RFC 7396 风格)与 preserve 两种;PostgreSQL 用 || 做“右值覆盖”式浅合并。
IF() 条件函数(MySQL)与跨库写法
MySQL 的 IF(cond, a, b) 是函数式三元表达式,能直接写在 SELECT 里;PostgreSQL 没有该表达式,统一用 CASE WHEN。
ABS / SIGN 绝对值与符号
取绝对值与判断正负:误差比较、金额方向、变动幅度计算的基础函数;SIGN 返回 -1、0、1。
REPEAT / REVERSE 重复与反转
重复字符串与反转字符串:造分隔线、生成占位、验证回文与编码问题时会用到。
数字格式化:FORMAT 与 to_char
给数字加千分位与小数位展示:MySQL 用 FORMAT(),PostgreSQL 用 to_char();输出都是字符串,仅用于展示层。
TRUNCATE 截断小数(注意与清表语句同名)
按位数直接截断、不做舍入:MySQL TRUNCATE(x,d)、PostgreSQL trunc(x,d);名字与 TRUNCATE TABLE 相同但不是一回事。
变更与事务
增删改、事务控制与隔离级别
UPDATE 基础与安全边界热门
更新满足条件的行:SET 支持多列赋值与表达式;漏写 WHERE 会更新全表,是生产事故的高频来源。
INSERT VALUES 插入数据
按列清单逐行或多行插入:显式列出列名可读且抗表结构变更;多行 VALUES 明显减少网络往返。
事务基础:BEGIN / COMMIT / ROLLBACK
把多条语句打包成原子操作:MySQL 用 START TRANSACTION 或 BEGIN,PostgreSQL 用 BEGIN;提交用 COMMIT,撤销用 ROLLBACK。
Upsert:ON DUPLICATE KEY UPDATE 与 ON CONFLICT
“存在则更新、不存在则插入”:MySQL 用 ON DUPLICATE KEY UPDATE,PostgreSQL 用 ON CONFLICT,是幂等写入的基础写法。
DELETE 基础与分批删除
按条件删除行:逐行记录日志、可回滚、触发 DELETE 触发器;大表删除要分批做,避免长事务与主从延迟。
CREATE TABLE 建表要点
建表时把主键、约束、字符集、引擎一次定清楚:MySQL 常用 InnoDB + utf8mb4;PostgreSQL 关注类型与约束表达。
事务隔离级别与设置方法
四档隔离级别控制并发读写行为:MySQL 默认 REPEATABLE READ,PostgreSQL 默认 READ COMMITTED;改级别可按会话或按事务。
锁读:SELECT FOR UPDATE / FOR SHARE
在事务里对行加锁再读写:解决“先查后改”的并发竞争;MySQL 与 PostgreSQL 语法接近,锁强度不同。
死锁的原理与应对
两个事务互相持有对方需要的锁形成环路:InnoDB 与 PostgreSQL 都会自动检测并回滚其中一个事务,应用侧必须能重试。
INSERT ... SELECT 拷数据
把查询结果直接写入目标表:数据搬迁、归档、宽表预计算的主力写法;不需要 VALUES 关键字。
LAST_INSERT_ID 与自增主键回填
取刚插入行的自增主键:MySQL 的 LAST_INSERT_ID() 是连接级值,不随其它连接变化;PostgreSQL 用 RETURNING 或 currval。
跨表更新:UPDATE ... FROM 与 UPDATE JOIN
用另一张表的数据更新本表:PostgreSQL 用 UPDATE ... FROM,MySQL 用 UPDATE ... JOIN;注意一对多匹配的不确定性。
ALTER TABLE 的锁与执行方式
结构变更的锁代价差异很大:MySQL 支持 INSTANT/INPLACE 减少锁表,PostgreSQL 多数操作会持有 ACCESS EXCLUSIVE。
RETURNING 返回插入结果(PostgreSQL)
插入/更新/删除后直接返回受影响的行数据:PostgreSQL 全支持;MySQL 没有 RETURNING,只能取自增 ID。
DELETE / TRUNCATE / DROP TABLE 对比
三种“删数据”的语义与代价差异明显:选错轻则慢,重则不可回滚丢结构。
autocommit 行为差异
MySQL 默认 autocommit=1(每条语句自动提交);PostgreSQL 没有该开关,天然“每条语句一个隐式事务”,除非显式 BEGIN。
长事务与悬挂事务的危害
长时间不提交的事务会拖住清理与统计:InnoDB 的 purge、PostgreSQL 的 vacuum 都被迫保留旧版本,还可能与 DDL 相互阻塞。
批量导入:LOAD DATA 与 COPY
从文件批量装载数据:MySQL 用 LOAD DATA INFILE(服务端/客户端本地),PostgreSQL 用 COPY(服务端)或 \copy(客户端)。
级联删除与外键动作
外键的 ON DELETE 动作决定父行删除时子行的命运:CASCADE 连带删除、SET NULL 置空、RESTRICT/NO ACTION 阻止删除。
隐式提交:DDL 与事务的边界
MySQL 里 DDL 语句会隐式提交当前事务;PostgreSQL 的 DDL 是事务性的、可以回滚,但持有更强的锁。
MVCC 与快照读:为什么两次查询结果不同
多版本并发控制让读不阻塞写:PostgreSQL 每个语句取快照(READ COMMITTED)、REPEATABLE READ 取事务快照;InnoDB 通过 undo 构造一致性读视图。
NOWAIT / SKIP LOCKED 锁读策略
锁读的两个变体:NOWAIT 拿不到锁立刻报错,SKIP LOCKED 跳过被锁行——后者是队列消费场景的常用模式。
排查锁等待:谁在阻塞谁
线上出现锁等待时先找出持锁会话与等待链:MySQL 用 performance_schema.data_lock_waits,PostgreSQL 用 pg_locks 与 pg_blocking_pids。
MERGE 语句(PostgreSQL 15+)
标准化的“合并”语句:按条件匹配则更新、不匹配则插入(还可 WHEN NOT MATCHED BY SOURCE 时删除);PostgreSQL 15 起支持,MySQL 无此语句。
MySQL 报错 1093:更新时查询同表
MySQL 不允许在 UPDATE/DELETE 的子查询里直接读同一张表;用派生表包一层或改 JOIN 绕开,是高频报错。
TRUNCATE TABLE 清空表的边界
快速清空全表:MySQL 里是 DDL(隐式提交、不可回滚),PostgreSQL 里可回滚但持有 ACCESS EXCLUSIVE 锁。
并发异常:脏读、不可重复读、幻读
三类典型并发异常与隔离级别的对应关系:级别越高异常越少,但冲突与重试成本上升。
锁等待与超时参数
控制等锁的耐心:MySQL 有 innodb_lock_wait_timeout;PostgreSQL 默认无限等待,需要显式设置 lock_timeout / statement_timeout。
CREATE INDEX 基础写法
建索引的常规语法与命名:单列、复合列顺序、唯一索引与删除索引;大表操作前先查看现有索引避免重复。
INSERT IGNORE 与 ON CONFLICT DO NOTHING
跳过冲突行的插入:MySQL 用 INSERT IGNORE,PostgreSQL 用 ON CONFLICT DO NOTHING;前者还会把其它错误降级为警告。
分批更新:UPDATE ORDER BY LIMIT 与 ctid 方案
大表更新采用分批提交:MySQL 支持 UPDATE ... ORDER BY ... LIMIT;PostgreSQL 没有 LIMIT,用 ctid/主键子查询分包。
删除关联数据:多表 DELETE 与 USING
按另一张表的条件删除:MySQL 用多表 DELETE ... FROM t1 JOIN t2,PostgreSQL 用 DELETE ... USING。
SAVEPOINT 部分回滚
事务内设置保存点,出错时只回滚到某个点而不是整个事务:批量处理中“跳过坏行、保留好行”的常用手段。
乐观锁:版本号条件更新
不提前加锁,用版本号在 UPDATE 时校验:更新影响行数为 0 说明发生并发修改,业务层决定重试或报冲突。
默认值、显式 NULL 与 DEFAULT 关键字
省略列取默认值,显式 NULL 存空值:两者语义不同;需要强制取默认值时用 DEFAULT 关键字。
PostgreSQL 分批删除写法与膨胀控制
PostgreSQL 没有 DELETE LIMIT:用 ctid 子查询分批;删除只是标记死元组,空间回收依赖 VACUUM,批量删除要配合清理策略。
SERIALIZABLE 的两种实现:SSI 与锁
同为 SERIALIZABLE:PostgreSQL 用 SSI(乐观检测、冲突中止),MySQL InnoDB 把普通读升级为共享锁(悲观、等待增多)。
加列:位置控制与成本差异
加列的写法与成本:MySQL 支持 FIRST/AFTER 指定位置;PostgreSQL 只能加在末尾(列顺序不可变)。
改列定义与改类型
修改列的类型/可空性/默认值:MySQL 用 MODIFY(可同时改名用 CHANGE),PostgreSQL 用 ALTER COLUMN 分动作执行。
REPLACE INTO 的真实语义(删除再插入)
MySQL 的 REPLACE 不是 UPDATE:遇到唯一键冲突先 DELETE 旧行再 INSERT 新行,未给出的列会重置为默认值。
间隙锁(Gap Lock)与幻读抑制
InnoDB 在 REPEATABLE READ 下用 next-key 锁(记录锁+间隙锁)锁住范围,防住别的事务在区间内插入;PostgreSQL 没有间隙锁。
并发失败重试模式
对死锁与序列化失败的标准处理:捕获特定错误、回滚、短暂退避后重试;重试需要幂等设计配合。
DROP TABLE 前的安全检查
删表不可逆:执行前确认外键引用、依赖对象与备份;PostgreSQL 的 CASCADE 会连视图一起删,MySQL 遇外键引用会拒绝。
受影响行数:changed 与 matched 的区别
确认 DML 实际影响:MySQL 默认返回“发生变化”的行数,开启 CLIENT_FOUND_ROWS 后返回“匹配到”的行数;PostgreSQL 返回处理行数。
RENAME TABLE 改名与影响面
表改名很快,但依赖它的对象与代码要同步:MySQL 用 RENAME TABLE(可一条语句多表原子换名),PostgreSQL 用 ALTER TABLE ... RENAME TO。
DROP INDEX 前的依赖检查
索引可能承担唯一约束或支撑外键:删除前确认约束依赖与使用情况,避免删出约束失效或外键报错。
外键创建与级联选项
用外键表达引用完整性:ON DELETE/UPDATE 决定父行变化时子行行为;外键列要建索引,写入路径也要评估锁影响。
CHECK 约束
用表达式限制列值范围:MySQL 8.0.16 起真正强制执行,PostgreSQL 长期支持;违反时写入直接报错。
只读事务的用途与写法
声明只读事务:给数据库优化空间、避免误写,适合报表、备份、批量导出连接。
生成列(虚拟与存储)
由其他列计算得出:虚拟列即时计算不占存储,存储列落盘、可建索引;常用于把表达式固化成列。
临时表的使用与边界
会话级表:适合拆分复杂批处理与中间结果;会话结束自动清理,注意连接池复用与事务语义差异。
CREATE VIEW 基础与物化视图
把常用查询固化为视图:简化访问与权限控制;注意普通视图不存数据,PG 需要物化视图才可落盘加速。
性能与执行计划
EXPLAIN 解读、索引策略与慢查询分析
EXPLAIN 查看执行计划
不真正执行语句就返回优化器选择的访问路径:每张表怎么访问、用哪个索引、预估多少行,是排查慢查询的常用起点。
EXPLAIN 关键列总览
id、type、key、key_len、rows、filtered、Extra 各列分别回答什么问题:理解列含义才能把输出读成有效信息。
PostgreSQL EXPLAIN 基础与 cost 解读
PG 的 EXPLAIN 输出计划树与启动/总成本估算:cost 是统计模型下的抽象单位,用于比较不同计划而非预测耗时。
EXPLAIN type 访问类型解读
从 const、eq_ref、ref、range 到 ALL:type 列描述每张表的访问方式,注意 index 与 ALL 都可能是整树/整表扫描。
联合索引的前缀使用规则
复合索引 (a,b,c) 只有从 a 开始连续使用才能有效:跳过前导列、或在中间列用范围,都会截断后续列的索引查找。
EXPLAIN Extra 常见标记解读
Using index、Using where、Using index condition、Using filesort、Using temporary:Extra 说明过滤与排序发生在哪一层。
B 树索引适用与不适用场景
B 树索引支持等值、范围、前缀匹配与排序;对列上做运算、前置通配、类型不匹配的条件帮不上忙。
EXPLAIN ANALYZE 实测统计
MySQL 8.0.18+ 会真正执行语句,输出每个节点的估算与实际 rows、耗时对比,用于发现估算偏差。
PG EXPLAIN ANALYZE 与 BUFFERS
ANALYZE 真正执行查询并给出实际行数、耗时与循环次数;BUFFERS 附加共享缓存命中与磁盘读取统计。
覆盖索引避免回表
查询需要的列都在索引中时,引擎可直接从索引返回结果(Extra 显示 Using index);InnoDB 二级索引天然包含主键列。
慢查询日志开启与查看
把执行时间超过 long_query_time 的语句记入日志文件:定位慢 SQL 的常规入口,可加开关同时记录未使用索引的语句。
列上做运算导致索引失效
WHERE 里对索引列做函数或运算时,B 树无法定位范围:改写成对常量做运算,或使用函数索引。
EXPLAIN FORMAT=TREE 与 FORMAT=JSON
TREE 把计划渲染成树形并带估算成本(8.0.16+),JSON 提供机器可读的成本明细,两者都可用于对比不同写法。
pg_stat_statements 找重查询
按语句模板累计调用次数与执行时间:PG 上做查询负载分析的基础扩展,需要预加载后启用。
ORDER BY 走索引消除排序
排序结果与索引顺序一致时可直接按序读索引:关注 EXPLAIN 是否出现排序步骤与回表代价。
隐式类型转换让索引失效
字符串列与数字比较时,列被隐式转为数字:索引无法使用;把字面量改成与列同类型即可。
key_len 判断复合索引用了几列
key_len 是索引查找实际用到的字节数:对照列定义宽度可以推断复合索引命中前几列,验证前缀使用情况。
performance_schema 按摘要找重查询
events_statements_summary_by_digest 按语句模板聚合调用次数与耗时:不用翻日志就能找出实例里的主要负载。
PG 扫描节点:Seq / Index / Bitmap
Seq Scan、Index Scan、Index Only Scan、Bitmap Heap Scan 各有适用面:识别计划树中的扫描方式判断瓶颈环节。
ANALYZE 更新统计信息
优化器依赖统计信息估算行数:数据大改动后计划变差,先 ANALYZE 再谈改写与加索引。
深分页优化:keyset 分页
OFFSET 很大时数据库仍要逐行跳过:改用“上一页最后一条的键”做条件,让每页都走索引定位。
rows 与 filtered 估算解读
rows 是预估扫描行数、filtered 是过滤后剩余百分比:两者相乘得到传给下一步的行数估算,用来定位低效过滤点。
sys schema 巡检视图
sys 把 performance_schema 原始数据整理成可读结果:全表扫描语句、表级 IO、未使用索引都有对应视图。
PG 连接节点:Nested Loop / Hash / Merge
计划树里三种连接算法各有适用场景:驱动集大小、内表索引、两侧有序性是选择依据。
索引条件下推 ICP
MySQL 5.6+ 把部分 WHERE 条件下推到存储引擎、在索引层先过滤(Extra: Using index condition),减少回表次数。
连接驱动顺序与索引配对
连接把“过滤后结果小”的一侧做驱动、另一侧连接列建索引:这是带来数量级差异的结构性因素。
批量写入调优要点
多行 INSERT、批提交事务、批量接口开启驱动改写:把行级往返与提交压缩成批级。
SHOW FULL PROCESSLIST 定位运行中查询
实时查看连接正在执行什么:结合 Time、State 与 Info 列找卡住的语句;输出只是当前时刻快照,需要多次观察。
LIKE 前置通配与替代方案
LIKE 'abc%' 可走索引,'%abc' 走不了:按业务选择全文检索、反转列或 trigram 索引等替代方案。
在线创建索引:INPLACE 与 CONCURRENTLY
生产环境加索引要控制阻塞:MySQL 8.0 用 ALGORITHM/LOCK 子句约束在线 DDL;PostgreSQL 用 CREATE INDEX CONCURRENTLY。
COUNT 的精确成本与近似替代
COUNT(*) 需要扫描符合条件的行:InnoDB 选择较小的索引扫描;海量数据考虑近似值与维护计数表。
消除排序步骤的常见手法
让 ORDER BY 能按索引顺序取数:列序列、方向、条件都要匹配;否则数据库要收集后排序。
按主键分批更新大表
大范围 UPDATE 会长时间持锁、生成大事务:按主键区间循环小批执行,控制锁与回滚规模。
EXPLAIN 中的表访问顺序
多表连接时 EXPLAIN 按访问顺序逐行列出:同 id 的行属于同一查询块,自上而下就是连接发生顺序。
PG 计划相关参数速览
work_mem、random_page_cost、effective_cache_size 影响计划选择:按实例与存储特性调整,enable_* 仅用于诊断。
基数与区分度:索引值不值得
Cardinality 是索引列不同值的估算数:区分度低(性别、状态)的单列索引帮助有限,组合列或改写更有效。
子查询改写为连接的取舍
IN/EXISTS 子查询与 JOIN 在多数优化器下可得到等价执行:语义差异(去重、NULL)决定改写方向。
避免 SELECT *
需要什么列就写什么列:减少 IO 与网络传输,保住覆盖索引机会,也让代码对表结构变更更稳健。
死锁日志解读与预防
SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段记录最近死锁的事务与等待;根本对策是统一加锁顺序、缩短事务。
执行计划突变排查思路
同一条 SQL 变慢但语句没改:先看计划是否变化,再查统计信息、数据分布与参数化因素。
mysqldumpslow 汇总慢日志
把慢日志按语句模板聚合排序:日志很大时先找出现次数多、总耗时高的模板,再回日志核对具体参数。
不可见索引:上线前验证删索引
MySQL 8.0 支持把索引设为 INVISIBLE:优化器忽略它但索引仍被维护,用于验证“删掉这个索引会不会变慢”。
全文索引与 MATCH AGAINST
按词做文本检索:MySQL 的 FULLTEXT(InnoDB 支持)与 PostgreSQL 的 tsvector + GIN 都能避开前置通配的全扫描。
GROUP BY 的执行方式与索引
分组可通过索引顺序、排序或哈希完成:EXPLAIN 显示临时表或排序时,优先考虑让分组借用索引。
UNION 与 UNION ALL 的成本差异
UNION 默认去重:需要临时表或排序去重;UNION ALL 直接拼接,配合外层排序与 LIMIT 更省。
sys.innodb_lock_waits 找阻塞源头
锁等待排查:等待方、阻塞方、阻塞语句与等待时长在一个视图里,直接定位谁在挡谁。
Handler 与 InnoDB 行计数解读
Handler_read_rnd_next、Innodb_rows_read 等计数从扫描行为侧面验证 EXPLAIN 的结论,用增量对比才有意义。
auto_explain 自动记录慢查询计划
把超过阈值的语句连同执行计划写进日志:事后分析线上慢查询时不用手动复现。
前缀索引:长字符串列的折中
长字符串列只索引前 N 个字符:减小索引体积,但无法覆盖查询,也不能完全替代整列排序用途。
OR 条件与索引利用
同列多个等值条件优先写成 IN;跨列 OR 往往触发索引合并或全表扫描,改写 UNION 更可控。
找出未使用与冗余索引
判断索引是否值得保留:MySQL 用 sys 视图,PostgreSQL 用 pg_stat_user_indexes 的扫描计数。
唯一索引与普通索引怎么选
唯一索引承担业务约束、等值查找命中即停;普通索引允许重复值、用于支撑查询。约束真与查询快是两件事。
派生表合并与物化(derived_merge)
MySQL 5.7+ 会把 FROM 子查询合并进外层查询(受阻时物化成临时表):用 EXPLAIN 辨别两种形态。
连接数与连接池观察
连接不是越多越好:数据库侧连接开销(线程或进程、内存)与应用池配置要匹配,短事务配够用的小池。
索引合并 index_merge
OR 或多条件组合命中多个单列索引时,优化器可分别扫描再合并结果(Extra: Using intersect/union);它常提示单列索引需要组合成复合索引。
NULL 值与索引使用
IS NULL 通常可走索引;负向条件(<>、NOT IN、NOT LIKE)容易退回扫描,能否走索引以实际计划为准。
PostgreSQL 表达式索引
把表达式结果建进索引:lower(email)、(timestamp_col::date) 等查询条件可直接命中,前提是表达式完全一致。
USE / IGNORE / FORCE INDEX 表提示
在表名后声明索引提示:强制、忽略或限定候选索引,用来复现计划或临时规避优化器误判。
MySQL 8.0 优化器提示 /*+ */
语句级提示:INDEX/NO_INDEX 控制索引、JOIN_ORDER 固定连接顺序、SET_VAR 临时改会话变量、MAX_EXECUTION_TIME 限制执行时长。
缓冲池命中率与物理读
Innodb_buffer_pool_reads 是磁盘读取页数、read_requests 是请求总数:两者比值反映读命中情况。
表空间回收与重建:OPTIMIZE / VACUUM FULL
大量删除后空间不一定归还:MySQL 的 OPTIMIZE TABLE 对 InnoDB 近似重建表;PostgreSQL 用 VACUUM FULL 或 pg_repack。
pt-query-digest 分析慢日志
Percona Toolkit 的聚合分析工具:按查询模板给出耗时分布、次数与示例,适合大日志的常态化分析。
JSON 多值索引(8.0.17+)
为 JSON 数组的每个元素建立索引条目:让 MEMBER OF、JSON_CONTAINS 等条件可以走索引。
跳跃扫描 skip scan(8.0.13+)
未使用联合索引前导列时,优化器可对前导列的少量取值逐个“跳跃”扫描:Extra 显示 Using index for skip scan。
降序索引(MySQL 8.0)
8.0 起支持真正的降序索引:多列混合排序方向(如 a ASC, b DESC)可被索引直接满足,避免额外排序。
PostgreSQL 部分索引
只索引满足条件的数据行:过滤条件固定、热点数据占比低的查询(如未完成订单)能显著缩小索引。
直方图统计(MySQL 8.0)
为无索引列或分布不均的列生成直方图:优化器据此估算过滤比例,改善访问路径选择。
只读副本延迟与读写分离注意
读副本承担只读流量时要监控复制延迟与一致性:写后立即读可能读到旧数据。
分区裁剪:让查询只扫目标分区
按分区键的常量条件定位分区:EXPLAIN 中会显示裁剪结果,分区键上的函数往往阻止裁剪。
哈希索引的适用边界
哈希索引只支持等值查找,不支持范围与排序:MySQL 的 Memory 引擎可显式建哈希索引,InnoDB 的自适应哈希索引由引擎内部管理。
PostgreSQL INCLUDE 覆盖列
把仅用于返回的列放进 INCLUDE 子句:既能支撑 Index Only Scan,又不参与键排序与去重。
InnoDB 持久化统计信息
统计信息落盘并可配置采样:8.0 默认持久化,重启后不丢;理解自动重算与采样页数有助于计划稳定。