我在 Debian x86 上安装了 PG 服务器(9.0.4)。
在我的一张桌子中,我使用numeric(6,1)
:
CREATE TABLE mytable(name text, numfield numeric(6,1))
Run Code Online (Sandbox Code Playgroud)
还为此字段创建了索引:
CREATE INDEX mytable_numfield_idx ON mytable USING btree (numfield);
Run Code Online (Sandbox Code Playgroud)
如果我将此字段与实数值一起使用,一切都会很好。正如我看到的查询使用索引:
EXPLAIN ANALYZE SELECT * FROM kodiall WHERE kodgo=123.0
Run Code Online (Sandbox Code Playgroud)
在 mytable 上使用 mytable_numfield_idx 进行索引扫描(cost=0.00..8.27 rows=1 width=193)(实际时间=0.085..0.087 rows=1 loops=1)
索引条件:(numfield = 123.0)
总运行时间:0.131 毫秒
但是如果我使用 0.0 或 NULL 作为条件值,由于某种原因索引被忽略:
EXPLAIN ANALYZE SELECT * FROM kodiall WHERE numfield=0.0
--EXPLAIN ANALYZE SELECT * FROM kodiall WHERE numfield=NULL
Run Code Online (Sandbox Code Playgroud)
mytable 上的 Seq Scan (cost=0.00..57.80 rows=1080 width=193) (实际时间=0.033..1.853 rows=1088 loops=1)
过滤器:(numfield = 0.0)
这里有什么问题?是0.0
不是numeric
值?
在现实查询我用LEFT JOIN
用numfield
,并在连接表的值可以为NULL。
在EXPLAIN
您查询时,首先,您可以看到规划器将总行数估计为 1
Index Scan using mytable_numfield_idx on mytable (cost=0.00..8.27 **rows=1** width=193)
Run Code Online (Sandbox Code Playgroud)
在这种情况下使用索引是有益的,因为几乎没有任何行符合您的WHERE
条件。
在第二种情况下,大约有1080 行符合您的条件。与顺序扫描相比,遍历每个索引的成本会很高。(更多的磁盘移动用于读取索引条目,然后接触实际的表行。)
Seq Scan on mytable (cost=0.00..57.80 **rows=1080** width=193)
Run Code Online (Sandbox Code Playgroud)
您仍然可以尝试通过暂时禁用顺序扫描来强制规划器对第二种情况使用索引。您可以通过以下方式实现:
BEGIN;
set enable_seqscan = off;
EXPLAIN ANALYZE SELECT * FROM kodiall WHERE numfield=0.0
COMMIT;
Run Code Online (Sandbox Code Playgroud)
与规划器选择的顺序扫描相比,上述语句所花费的实际时间可能更多。
注意:顺序扫描并不总是坏的。如果您选择表的一个主要块,读取整个表并过滤行比在索引页和表本身之间跳转要快。