首页/数据/SQL 速查/PostgreSQL 表达式索引
性能与执行计划72 条

PostgreSQL 表达式索引

把表达式结果建进索引:lower(email)、(timestamp_col::date) 等查询条件可直接命中,前提是表达式完全一致。

SQL 速查数据更新于 2026-10-13

表达式索引是什么

PostgreSQL 的表达式索引把表达式的结果建进索引,例如对邮箱做小写转换后再建索引,或把时间戳转成日期后建索引。只有当查询条件与索引表达式完全一致时才能命中,多包一层函数都用不上。表达式必须是不可变函数,依赖当前时间的函数不能用于索引。

基本信息

适用平台PostgreSQL
语法CREATE INDEX 名 ON 表 (表达式)
命中条件查询里的表达式要与索引表达式一致,函数、顺序都要对上
约束表达式必须是 IMMUTABLE 函数
MySQL 对应8.0.13+ 的功能索引,表达式需用括号包裹

典型使用场景

  • 邮箱、用户名做大小写不敏感查询
  • 把时间戳按日期建索引做按天统计
  • 对 JSON 字段的抽取结果建索引
  • 对拼接后的字段建索引
  • 统一历史数据里大小写不一致的检索

示例

示例
-- 建表达式索引
CREATE INDEX idx_users_lower_email ON users (lower(email));

-- 命中:查询表达式与索引一致
SELECT * FROM users WHERE lower(email) = 'a@x.com';

-- 不命中:查询端多包了一层 trim
SELECT * FROM users WHERE trim(lower(email)) = 'a@x.com';

-- 按日期统计的场景:对转换后的表达式建索引
CREATE INDEX idx_orders_day ON orders ((created_at::date));
SELECT count(*) FROM orders WHERE created_at::date = DATE '2024-06-01';

两库对照

维度PostgreSQLMySQL
语法CREATE INDEX … (表达式)功能索引,表达式需括号包裹
命中要求表达式完全一致同样要求一致
函数要求必须是 IMMUTABLE表达式需为确定性
版本长期支持8.0.13 起
典型场景小写转换、日期截断、JSON 抽取同
索引表达式与查询表达式必须逐字符一致:lower(email) 的索引不会被 trim(lower(email)) 命中,写法稍微一变就等于白建。
不可变函数的限制来自索引的持久性:索引里存的是计算结果,函数行为必须稳定。需要按当前时间过滤的查询没法直接建索引,通常改用分区或预计算列。
表达式索引也能做成唯一索引:例如对转换后的邮箱建唯一索引,用来防止大小写不同的重复注册,普通唯一索引拦不住这种情况。
改表达式就等于换索引:旧索引不会自动更新,通常先建新索引再删旧的,期间两条索引并存会额外占用空间并加重写入开销。

易错点

  • 用不满足不可变条件的函数建索引:会被直接拒绝,建索引前先确认函数的稳定性属性
  • 查询端换了等价但不同的写法:lower(email) 的索引不会被 trim(lower(email)) 命中
  • 以为等价表达式一定能命中:优化器只匹配写法一致的表达式
  • 在 MySQL 里照抄语法:功能索引要求表达式用括号包裹,写法不同
  • 建完表达式索引不验证:要用 EXPLAIN 确认查询确实走了这个索引

数据来源与参考

  • PostgreSQL Documentation · Indexes on Expressions
  • PostgreSQL Documentation · Function Volatility Categories
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌