文章目录
环境
系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:4.5
文档用途
本文档主要介绍对字段添加类型转换,会导致SQL不走索引,从而影响查询性能。
详细信息
表的结构说明
表 table_varchar_id 的字段 id 是 varchar 类型,存在索引 idx_table_varchar_id 。
表 table_int_id 的字段 id 是 bigint 类型,存在索引 idx_table_int_id 。
执行计划示例
sql
highgo=# EXPLAIN (ANALYZE, BUFFERS)
SELECT v.id as varchar_id, i.id as int_id, v.col1, i.col1
FROM table_varchar_id v
, table_int_id i
WHERE CAST(v.id AS BIGINT) = i.id
AND i.id BETWEEN 100000000000000000 AND 100000000005000000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=8.45..586.02 rows=7 width=53) (actual time=0.026..2.209 rows=5 loops=1)
Hash Cond: ((v.id)::bigint = i.id)
Buffers: shared hit=418
-> Seq Scan on table_varchar_id v (cost=0.00..515.00 rows=10000 width=32) (actual time=0.004..0.806 rows=10000 loops=1)
Buffers: shared hit=415
-> Hash (cost=8.38..8.38 rows=5 width=21) (actual time=0.012..0.012 rows=5 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
Buffers: shared hit=3
-> Index Scan using idx_table_int_id on table_int_id i (cost=0.28..8.38 rows=5 width=21) (actual time=0.009..0.010 rows=5 loops=1)
Index Cond: ((id >= '100000000000000000'::bigint) AND (id <= '100000000005000000'::bigint))
Buffers: shared hit=3
Planning Time: 0.117 ms
Execution Time: 2.237 ms
(13 行记录)
上面执行计划中,查询 table_varchar_id 表时,执行了顺序扫描,没有使用 id 字段的索引,如下所示:
sql
-> Seq Scan on table_varchar_id v (cost=0.00..515.00 rows=10000 width=32) (actual time=0.004..0.806 rows=10000 loops=1)
Buffers: shared hit=415
原因
SQL语句中对 table_varchar_id 表的字段 id 添加了 CAST(v.id AS BIGINT) 类型转换,导致没有执行 id 字段的索引。
方案
方案一:修改表字段的数据类型
针对 table_varchar_id 表的字段 id ,确认业务上常用的数据类型是数值型还是字符串型,然后统一修改为相同的数据类型,示例如下:
sql
highgo=# alter table table_varchar_id alter COLUMN id type bigint using id::bigint; --统一使用 bigint 类型
highgo=# analyze table_varchar_id; --收集表的统计信息,保证被修改数据类型的 id 字段的索引立即生效
数据类型修改统一后,SQL语句中的 cast 转换可以删除。
方案二:创建 cast 类型转换的索引
sql
highgo=# create index idx_table_varchar_cast_id on table_varchar_id(cast(id as bigint));
highgo=# analyze table_varchar_id; --收集表的统计信息,保证类型转换索引、表达式索引立即生效
注:类型转换索引、表达式索引的维护成本比较高,尤其是数据变更频繁的表,影响性能
方案三:更换为对条件值进行类型转换
SQL语句中 table_varchar_id 表的字段 id 去掉 cast 转换,表 table_int_id 的字段 id 作为条件值添加 varchar 类型的转换,修改后的SQL及执行计划如下:
sql
highgo=# EXPLAIN (ANALYZE, BUFFERS)
SELECT v.id as varchar_id, i.id as int_id, v.col1, i.col1
FROM table_varchar_id v
, table_int_id i
WHERE v.id = i.id::varchar --i.id 字段添加 varchar 类型转换,v.id 可以执行索引扫描
AND i.id BETWEEN 100000000000000000 AND 100000000005000000;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.57..49.97 rows=5 width=53) (actual time=0.021..0.029 rows=5 loops=1)
Buffers: shared hit=16 read=2
-> Index Scan using idx_table_int_id on table_int_id i (cost=0.28..8.38 rows=5 width=21) (actual time=0.003..0.004 rows=5 loops=1)
Index Cond: ((id >= '100000000000000000'::bigint) AND (id <= '100000000005000000'::bigint))
Buffers: shared hit=3
-> Index Scan using idx_table_varchar_id on table_varchar_id v (cost=0.29..8.31 rows=1 width=32) (actual time=0.004..0.004 rows=1 loops=5)
Index Cond: ((id)::text = ((i.id)::character varying)::text)
Buffers: shared hit=13 read=2
Planning Time: 0.123 ms
Execution Time: 0.048 ms
(10 行记录)