PostgreSQL查询显然不使用多列索引

时间:2013-02-03 17:21:25

标签: postgresql query-optimization

运行Heroku“Crane”PostgreSQL实例(版本9.1.6)

我有一张带销售点的桌子;货币金额以当地货币计算。我有一个货币换算表,其中包含每种货币和欧元之间的转换因子。我想总结给定书籍(产品)的销售额,退货,赠品和收入(以美元计)。所以我加入货币转换表一次将当地货币兑换成欧元,然后再将欧元转换成美元(请记住,根据销售的结算日期,费率会有所不同)。因此,要考虑的每个销售点将与货币转换两次加入;实验告诉我,这是主要的减速因素。

所以我正在尝试优化以下查询:

SELECT
    sum(paid_sales - paid_returns) as paid_units,
    sum(royalty_amt*(uu_cc.rate / sp_cc.rate)) as royalty_amt, 
    sum(free_sales - free_returns) as free_units,
    sum(lent_units) as lent_units 
  FROM "sales_points" 
  join currency_conversions sp_cc
    on sp_cc.date = sales_points.settlement_date
   and sp_cc.currency = sales_points.currency 
  join currency_conversions uu_cc
    on uu_cc.date = sales_points.settlement_date
   and uu_cc.currency = 'USD' 
  WHERE "sales_points"."book_id" = 234
  LIMIT 1

我创建了以下索引:

CREATE INDEX index_currency_conversions_on_date_and_currency
  ON currency_conversions
  USING btree (date, currency COLLATE pg_catalog."default");

然而EXPLAIN(在运行ANALYZE之后)告诉我它正在对货币转换表进行顺序扫描。如果重要,date的类型为'date',currency的类型为'char var(255)'。

以下是查询计划:

Limit  (cost=7285.04..7285.04 rows=1 width=39) (actual time=103.166..103.167 rows=1 loops=1)
  Buffers: shared hit=916
  ->  Aggregate  (cost=7285.04..7285.04 rows=1 width=39) (actual time=103.163..103.163 rows=1 loops=1)
        Buffers: shared hit=916
        ->  Hash Join  (cost=584.15..7256.29 rows=6388 width=39) (actual time=60.513..92.084 rows=5840 loops=1)
              Hash Cond: (sp_cc.date = uu_cc.date)
              Buffers: shared hit=916
              ->  Hash Join  (cost=351.63..6985.45 rows=6388 width=39) (actual time=52.454..72.418 rows=5840 loops=1)
                    Hash Cond: ((sales_points.settlement_date = sp_cc.date) AND ((sales_points.currency)::text = (sp_cc.currency)::text))
                    Buffers: shared hit=763
                    ->  Bitmap Heap Scan on sales_points  (cost=54.09..6630.06 rows=6446 width=30) (actual time=0.912..7.020 rows=5840 loops=1)
                          Recheck Cond: (book_id = 234)
                          Buffers: shared hit=610
                          ->  Bitmap Index Scan on index_sales_points_on_book_id  (cost=0.00..53.77 rows=6446 width=0) (actual time=0.809..0.809 rows=6521 loops=1)
                                Index Cond: (book_id = 234)
                                Buffers: shared hit=22
                    ->  Hash  (cost=214.95..214.95 rows=20649 width=16) (actual time=51.502..51.502 rows=20649 loops=1)
                          Buckets: 4096  Batches: 1  Memory Usage: 968kB
                          Buffers: shared hit=153
                          ->  Seq Scan on currency_conversions sp_cc  (cost=0.00..214.95 rows=20649 width=16) (actual time=0.007..21.153 rows=20649 loops=1)
                                Buffers: shared hit=153
              ->  Hash  (cost=225.27..225.27 rows=2071 width=12) (actual time=8.040..8.040 rows=2071 loops=1)
                    Buckets: 1024  Batches: 1  Memory Usage: 89kB
                    Buffers: shared hit=153
                    ->  Seq Scan on currency_conversions uu_cc  (cost=0.00..225.27 rows=2071 width=12) (actual time=0.021..5.963 rows=2071 loops=1)
                          Filter: ((currency)::text = 'USD'::text)
                          Buffers: shared hit=153
Total runtime: 103.306 ms

有谁知道为什么它没有使用我的索引?

1 个答案:

答案 0 :(得分:0)

多列索引在这里是个错误。您可能希望在两列上有两个单独的索引,因为这为计划程序提供了更大的灵活性。

您当前的索引不能与您的查询一起使用,因为它需要从表中查询日期(btree是第一个日期,其次是货币)。如果列以其他顺序排列,则可能有用,但在日期更具选择性的情况下无法使用。

您最好的选择是为这两个字段分别设置索引。通过这种方式,规划器可以选择哪个索引对于手头的查询更具选择性,而不是必须接受或留下对于给定查询可能具有可疑值的索引。

另请注意,PostgreSQL可以跨多个索引进行位图索引扫描,允许它在必要时同时使用这两个索引。