为什么在SELECT COUNT(DISTINCT)中不使用索引?

时间:2019-07-17 15:38:13

标签: postgresql

这就是我在PostgreSQL 10.9(public class ProductLogDaoTest { ProductLogDao instance = new ProductLogDao(); RequestDTO requestDTO = Mockito.mock(RequestDTO.class); JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class); Map<String, String> requestParamMap = new HashMap<>(); @Before public void setUp() throws Exception { instance.setJdbcTemplate(jdbcTemplate); } @Test public void getProductLogDetail_W_Custom() throws SQLException, DataAccessException { when(requestDTO.getParameters()).thenReturn(requestParamMap); requestParamMap.put("recordtype", "custom"); assertNotNull(instance.getProductLogDetail(requestDTO)); } } x)中所做的事情:

VARCHAR(100)

该表有超过150万条记录,并且具有索引:

SELECT COUNT(DISTINCT x) FROM t

请求耗时7秒以上。 CREA­TE INDE­X idx_1 ON t­ USIN­G btre­e (x) 就是这样:

EXPLAIN

怎么了?为什么不使用索引?

2 个答案:

答案 0 :(得分:2)

这里的问题是,尽管在列t上确实有一个B树索引,但它不一定有助于找到不同的计数。假设索引在概念上看起来像这样:

1 - 1 - 2 - 2 - 2 - 2 - 4 - 4 - 9

如果只需要最小和最大值,则可以使用理论上的索引,因为第一个和最后一个值都包含此信息,因此不需要扫描。但是,要找到所有不同的值,必须进行索引扫描。请注意,建立索引实际上并没有帮助,因为Postgres仍然必须触摸t列中的每个值才能获得答案。

COUNT是一个聚集函数,它倾向于索引不友好(与MINMAX可以是索引友好的)不同。

答案 1 :(得分:2)

这取决于两个因素:

  1. 表是否具有“宽行”
  2. 桌子是否被吸尘

无论如何,查询将必须扫描整个索引或整个表,因为PostgreSQL中没有没有索引跳过扫描

PostgreSQL可以扫描索引或表。

  • 如果表最近没有被清理,则索引扫描将总是必须访问该表以确定该行是否可见。在这种情况下,顺序扫描将始终更快。

  • 如果表最近被清理干净,并且可见性图的大多数块都标记为“全部可见”,则您可以进行仅索引扫描。 / p>

    • 如果表的行很窄,则只进行索引扫描的可能性较小,因为那样一来,读取索引不会比读取表便宜(顺序读取会更快)。

      < / li>
    • 对于具有宽行的表,您将获得仅索引扫描。