eclipselink jpa使用COUNT(id)生成计数查询,而不是COUNT(*)

时间:2015-11-02 14:53:26

标签: java postgresql hibernate jpa spring-data-jpa

我正在使用Eclipselink,Spring Data和Postgresql。在我的项目中,我注意到当使用SpringData存储库提供的分页结果时,会出现如下问题:

SELECT COUNT(id) 
FROM table 
WHERE [part generated according to specification]

其中" id"是" table"的主键。挖掘解释我发现对于一个非常大的表,COUNT(id)比COUNT()慢大约10倍(count(id)在" id"列中查找非空值()只返回符合条件的行数),count(*)也可以在count(id)时使用索引 - 不是。

我跟踪了SpringData基本存储库类,似乎只有JPA实现负责此查询生成。

  1. 使用count(id)代替更快的COUNT(*)?
  2. 的原因是什么
  3. 我可以改变这种行为(无论如何 - 甚至增强现有组件)?
  4. 任何帮助表示赞赏

    - [编辑] -

    有一张桌子:

    \d ord_order
                                           Table "public.ord_order"
             Column          |           Type            |                       Modificators
    -------------------------+--------------------------+----------------------------------------------------------
     id                      | integer                  | NOT NULL DEFAULT nextval('ord_order_id_seq'::regclass)
     test_order              | boolean                  | DEFAULT false
    ...
    Indexes:
        "pk_order" PRIMARY KEY, btree (id)
        "idx_test_order" btree (test_order)
    
    
    
    # explain SELECT COUNT(*) FROM ord_order WHERE (test_order = false);
                                    QUERY PLAN
    --------------------------------------------------------------------------
     Aggregate  (cost=89898.79..89898.80 rows=1 width=0)
       ->  Index Only Scan using idx_test_order on ord_order  (cost=0.43..85375.37 rows=1809366 width=0)
             Index Cond: (test_order = false)
             Filter: (NOT test_order)
    (4 wiersze)
    
    
    
    # explain SELECT COUNT(id) FROM ord_order WHERE (test_order = false);
                                    QUERY PLAN
    --------------------------------------------------------------------------
     Aggregate  (cost=712924.52..712924.53 rows=1 width=4)
       ->  Seq Scan on ord_order  (cost=0.00..708401.10 rows=1809366 width=4)
             Filter: (NOT test_order)
    (3 wiersze)
    

    现在差异是~90k vs~713k以及索引扫描与全扫描

2 个答案:

答案 0 :(得分:0)

count(*)可以使用索引,因为查询中只引用了一列(test_order)。 count(id)引用了两列,因此Postgres必须选择id test_order列才能构建结果。

正如我已经提到的,当对查询没有限制时,有些人认为count(id)count(*) - 更快。对于任何具有不错优化程序的DBMS来说,这个神话从来都不是真的。我想这就是为什么你的混淆层使用count(id)而不是count(*)的原因。

假设您不想摆脱ORM(重新获得对应用程序正在使用的SQL的控制权),我可以看到的唯一解决方法是创建Postgres可以使用的部分索引:

create index on ord_order (id)
where test_order = false;

答案 1 :(得分:0)

我设法使用该实现提供自定义Spring Data Repository基类实现和工厂。结果生成的计数查询现在具有以下形式:

SELECT COUNT(1) FROM table

与COUNT(*)具有相同的计划。这似乎是一个很好的解决方案,适用于应用程序中所有已定义的存储库。

我不知道如何生成COUNT(*),COUNT(1)更容易,因为COUNT函数需要一些表达式作为参数,我可以提供静态值 - 1