SQL中的函数索引/表达式索引
在大多数数据库系统中,"函数索引"和"表达式索引"指的是同一个概念。它们都是指对一个表达式(而不仅仅是列名)的结果创建的索引。
下面我们以几个主流数据库为例来详细说明。
1. 什么是函数索引/表达式索引?
标准的索引是针对一个或多个列的原始值创建的。例如:
sql
CREATE INDEX idx_employee_name ON employees (last_name);
这个索引 idx_employee_name 会存储 last_name 列的值,并快速定位到特定的姓氏。
而函数索引(或表达式索引) 是针对一个表达式的结果创建的。这个表达式通常包含一个或多个列,并经过函数处理或计算。
例如,如果你想忽略大小写地搜索姓氏,可以创建一个这样的索引:
sql
-- 在 PostgreSQL 中
CREATE INDEX idx_employee_name_lower ON employees (LOWER(last_name));
这个索引 idx_employee_name_lower 存储的不再是 'Smith',而是 'smith'。当你使用 LOWER(last_name) 进行查询时,数据库就可以利用这个索引。
2. 为什么需要它?(主要优点)
- 提升查询性能 :当你的
WHERE子句、JOIN条件或ORDER BY子句中使用了函数或表达式时,普通索引将无法被使用,导致全表扫描。函数索引可以让这类查询利用索引,大幅提高速度。 - 强制数据完整性:可以创建一个基于函数的唯一索引,来保证表达式级别的唯一性。例如,确保邮箱地址的小写形式是唯一的。
3. 如何使用?(语法示例)
不同的数据库系统语法略有不同,但核心思想一致。
PostgreSQL
PostgreSQL 官方文档中通常使用"表达式索引"这个术语。
sql
-- 1. 不区分大小写的查询
CREATE INDEX idx_user_email_lower ON users (LOWER(email));
-- 查询时,这样就能用上索引
SELECT * FROM users WHERE LOWER(email) = LOWER('SomeOne@Example.COM');
-- 2. 对日期进行查询(例如,只按日期部分查询,忽略时间)
CREATE INDEX idx_orders_order_date ON orders (DATE(order_timestamp));
SELECT * FROM orders WHERE DATE(order_timestamp) = '2023-10-25';
-- 3. 对两个列进行计算后索引
CREATE INDEX idx_products_total_value ON products (unit_price * quantity_in_stock);
SELECT * FROM products WHERE (unit_price * quantity_in_stock) > 100;
-- 4. 创建唯一的表达式索引
CREATE UNIQUE INDEX idx_users_unique_username ON users (LOWER(username));
-- 这将阻止插入 'JohnDoe' 和 'johndoe',因为它们的 LOWER() 结果相同。
MySQL
MySQL 从 8.0.13 版本开始支持函数索引(在之前版本可以通过生成列来模拟)。
sql
-- 1. 不区分大小写的索引
CREATE INDEX idx_employee_name_lower ON employees ((LOWER(last_name)));
-- 注意:MySQL 8.0.13+ 要求表达式用括号括起来
-- 2. 对日期部分创建索引
CREATE INDEX idx_sales_date ON sales ((DATE(sale_datetime)));
在 MySQL 5.7 及更早版本中的变通方法(使用生成列):
sql
-- 先添加一个虚拟的生成列
ALTER TABLE employees
ADD COLUMN last_name_lower VARCHAR(100) AS (LOWER(last_name)) VIRTUAL;
-- 然后在这个生成列上创建普通索引
CREATE INDEX idx_employee_name_lower ON employees (last_name_lower);
Oracle
Oracle 也支持函数索引。
sql
-- 1. 不区分大小写的索引
CREATE INDEX idx_emp_name_lower ON employees (LOWER(last_name));
-- 2. 使用函数创建唯一约束
CREATE UNIQUE INDEX idx_prod_code ON products (UPPER(product_code));
4. 注意事项和缺点
- 维护开销:和普通索引一样,函数索引会在插入、更新和删除数据时带来额外的开销,因为索引也需要被维护。
- 精确匹配 :查询必须精确地 使用索引定义中相同的表达式,优化器才能识别并使用它。
- 能用上索引 :
SELECT ... WHERE LOWER(name) = 'alice' - 用不上索引 :
SELECT ... WHERE name = 'Alice'(即使name是'Alice',也不会使用LOWER(name)上的索引)
- 能用上索引 :
- 数据库支持:并非所有数据库都原生支持。请查阅您所用数据库的文档。
- 大小写敏感性 :索引表达式本身可能是大小写敏感的(例如,在 PostgreSQL 中
LOWER()和UPPER()是不同的)。确保你的查询和索引使用相同的函数。
总结
| 特性 | 普通索引 | 函数索引/表达式索引 |
|---|---|---|
| 索引内容 | 列的原始值 | 对列应用函数或计算后的结果 |
| 适用场景 | WHERE column = 'value' |
WHERE FUNCTION(column) = 'value' |
| 优点 | 结构简单,维护直接 | 极大优化了包含函数/表达式的查询 |
| 缺点 | 无法优化复杂条件的查询 | 维护成本稍高,查询必须精确匹配表达式 |
核心思想:如果你发现某些包含函数或表达式的查询速度很慢,并且这些查询频繁执行,那么为这些表达式创建一个函数索引是一个非常有效的优化手段。