【问题标题】:Poor performance on contained by query for tstzrangetstzrange 查询包含的性能不佳
【发布时间】:2014-09-10 18:18:22
【问题描述】:

我有两张桌子:

account_transaction:

+-------------------------------+--------------------------+------------------------+
|            Column             |           Type           |       Modifiers        |
+-------------------------------+--------------------------+------------------------+
| id                            | integer                  | not null               |
| account_id                    | bigint                   | not null               |
| created                       | timestamp with time zone | not null default now() |
| transaction_type              | text                     | not null               |
| amount                        | numeric(5,2)             | not null               |
| external_reference_id         | character varying(60)    |                        |
+-------------------------------+--------------------------+------------------------+

索引:

"idx_account_transaction_created" btree (created)

reporting_period:

+------------+--------------------------+-----------+
|   Column   |           Type           | Modifiers |
+------------+--------------------------+-----------+
| month      | text                     |           |
| created    | timestamp with time zone |           |
| date_range | tstzrange                |           |
+------------+--------------------------+-----------+

我想获取上一个报告期间的所有交易。这是产生相同结果的两个查询,但一个执行 seq 扫描,另一个可以使用 idx_account_transaction_created 索引。

explain select count(*) from account_transaction where created <@ (select date_range from reporting_period order by created desc limit 1);
+----------------------------------------------------------------------------------------+
|                                       QUERY PLAN                                       |
+----------------------------------------------------------------------------------------+
| Aggregate  (cost=4214.81..4214.82 rows=1 width=0)                                      |
|   InitPlan 1 (returns $0)                                                              |
|     ->  Limit  (cost=13.20..13.20 rows=1 width=40)                                     |
|           ->  Sort  (cost=13.20..13.60 rows=800 width=40)                              |
|                 Sort Key: reporting_period.created                                     |
|                 ->  Seq Scan on reporting_period  (cost=0.00..12.40 rows=800 width=40) |
|   ->  Seq Scan on account_transaction  (cost=0.00..4200.81 rows=1602 width=0)          |
|         Filter: (created <@ $0)                                                        |
+----------------------------------------------------------------------------------------+
(8 rows)

explain select count(*) from account_transaction where created >= '2014-06-01' and created <= '2014-06-30 23:59:59.999999';
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                            QUERY PLAN                                                                            |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Aggregate  (cost=2640.54..2640.54 rows=1 width=0)                                                                                                                |
|   ->  Index Only Scan using idx_account_transaction_created on account_transaction  (cost=0.08..2605.77 rows=69535 width=0)                                      |
|         Index Cond: ((created >= '2014-06-01 00:00:00+00'::timestamp with time zone) AND (created <= '2014-06-30 23:59:59.999999+00'::timestamp with time zone)) |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
(3 rows)

我更喜欢第一个查询,因为它看起来更容易阅读和理解,而且它只有 1 次往返。第二个效率更高,因为它使用 created 字段上的索引,但这意味着应用程序需要走出去获取最后一个报告期间并获取 date_range 字段的上下边界(不是最糟糕的事情世界)。我想我总是可以把它写成一个函数或一个视图。但是,我有点惊讶 PostgreSQL 没有弄清楚它可以使用索引。我在这里缺少什么吗?有没有办法让第一个查询使用索引?

我使用的是 PostgreSQL 9.3

【问题讨论】:

    标签: sql postgresql indexing range postgresql-9.3


    【解决方案1】:

    运算符&lt;@ 需要使用 GIN 或 GiST 索引。不适用于普通的 B 树索引。
    Details in the manual here.
    相关答案:

    另类

    对于您的用例,B 树索引可能更有效。这应该允许 Postgres 使用它:

    SELECT count(*) AS ct
    FROM  (
       SELECT lower(date_range) AS ts_from, upper(date_range) AS ts_to
       FROM   reporting_period
       ORDER  BY created DESC
       LIMIT  1
       ) r
    JOIN   account_transaction a ON a.created >= r.ts_from
                                AND a.created <  r.ts_to
    ;
    

    假设您的所有tstzrange 值都有包括 下限和不包括 上限(建议的默认值)。为了强制执行,我建议在您的表 reporting_period 中使用 CHECK 约束:

    CHECK (lower_inc(date_range) AND NOT upper_inc(date_range))
    

    否则,您需要更详细的条件。相关答案:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-22
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      • 1970-01-01
      • 2016-06-21
      相关资源
      最近更新 更多