PostgreSQL中通过pg_indexes表的indexdef字段可直接查看完整索引定义,SQL Server则需联查sys.indexes和sys.index_columns并用is_included_column=1区分INCLUDE列。

如何用 pg_indexes 或 sys.indexes 查看索引定义
PostgreSQL 和 SQL Server 的系统视图是查索引覆盖最直接的入口。别依赖 EXPLAIN 临时推断——先确认索引本身长什么样,否则容易误判“覆盖”。
- PostgreSQL:查
pg_indexes表,重点关注indexdef字段,它给出完整 CREATE INDEX 语句,能一眼看出包含列(INCLUDE)和排序列 - SQL Server:查
sys.indexes+sys.index_columns,用is_included_column = 1区分主键列和INCLUDE列 - MySQL 不支持
INCLUDE,所谓“覆盖”只能靠联合索引把 WHERE + SELECT 所有字段全塞进索引列里,顺序还必须匹配最左前缀原则
怎么判断一个查询是否真被索引覆盖
关键不是“有没有索引”,而是执行计划里是否出现 Index Only Scan(PostgreSQL)或 INDEX ONLY 提示(SQL Server),且 Heap Fetches 为 0 或 Key Lookup 消失。
- PostgreSQL:运行
EXPLAIN (ANALYZE, BUFFERS),如果看到Heap Fetches: 0,说明没回表;若数值 > 0,说明即使有索引,仍需访问堆表取数据 - SQL Server:看执行计划 XML 中是否有
<RelOp LogicalOp="Index Scan" ...>且无Bookmark Lookup或Key Lookup - WHERE 条件字段必须是索引的前导列(不能跳过),SELECT 字段要么在索引列中,要么全在
INCLUDE里——漏一个,就破功
INCLUDE 列写错顺序或类型会失效吗
会,但不是语法报错,而是悄悄退化成非覆盖索引。尤其容易忽略的是数据类型隐式转换和表达式使用。
-
INCLUDE列顺序无关,但类型必须和 SELECT 中字段完全一致(比如varchar(50)对varchar(100)可能触发隐式转换,导致无法覆盖) - SELECT 中用了函数,比如
UPPER(name),那INCLUDE (name)就不生效——得建函数索引或把表达式放进INCLUDE(部分数据库不支持) - NULL 值处理:某些旧版 PostgreSQL 对
INCLUDE列含 NULL 的行仍可能触发 Heap Fetch,建议配合WHERE col IS NOT NULL验证
为什么 SELECT * 几乎不可能被覆盖
除非你给每个字段都建了 INCLUDE,否则 * 必然引入未索引列。更麻烦的是,表结构变更后,这个“覆盖”会无声失效。
- 明确列出需要的字段,哪怕多打几个字,也比
SELECT *安全 - 如果业务确实要动态列,考虑应用层拼字段,或用物化视图预聚合,而不是硬靠索引覆盖
- 注意
TOAST字段(如 PostgreSQL 的TEXT、JSONB):即使INCLUDE了,也可能因实际存储在 toast 表而触发额外 IO

















