代码之家  ›  专栏  ›  技术社区  ›  hendrikbeck

不带WHERE的PostgreSQL查询只有ORDER BY和LIMIT不使用索引

  •  3
  • hendrikbeck  · 技术社区  · 6 年前

    我有一个表,其中包含一个BIGSERIAL类型的“id”列。我还为这一列建立了索引(排序顺序降序、BTREE、unique)。

    我经常需要从一个包含数百万条目的表格中检索最后10条、20条、30条,如下所示:

    SELECT * FROM table ORDER BY id DESC LIMIT 10
    

    我认为这是一个非常明显的例子:这个特定字段有一个索引,排序顺序匹配,我只需要10个条目,而整个表中有数百万个条目,这个查询肯定使用索引扫描。

    但它不会,它会对整个桌子进行顺序扫描。

    我试着深入挖掘,没有发现任何异常。博士后博士 https://www.postgresql.org/docs/9.6/static/indexes-ordering.html 说:

    一个重要的特例是ORDER BY与LIMIT n:An的组合 显式排序必须处理所有数据才能识别第一个 n行,但如果有一个索引与ORDER BY匹配,则前n行 可以直接检索行,而无需扫描其余的行。

    但它仍然不起作用。有人给我指点什么吗?也许我再也看不到森林里的福雷斯了-(

    2 回复  |  直到 6 年前
        1
  •  2
  •   hendrikbeck    6 年前

    好吧,大声说出来,并试图收集更多的信息,将其放入我的问题中,显然让我再次看到了福雷斯,我发现了真正的问题。在我上面提到的文件中,下面是这句话:

    以升序存储且先为空的索引可以满足以下任一条件: 先按x ASC空值排序,或最后按x DESC空值排序,具体取决于 扫描的方向。

    这就是问题所在。我在索引中指定了排序顺序,但我先忽略了空值,后忽略了空值。

    如果在查询中没有明确提及,Postgres默认值首先为空。所以Postgres首先发现的是DESC NULLS的组合顺序,这在我的索引中没有涵盖。排序顺序和空值的组合才是重要的。

    两种可能的解决方案:

    • 或者在查询中相应地先/后提到null,以便与索引匹配
    • ...或者先把索引改为空(我就是这么做的)

    现在Postgres正在进行适当的索引扫描,在查询过程中只涉及10个元素,而不是所有元素。

        2
  •  0
  •   RoshT54    6 年前

    如果需要获取表中的最后10个条目,可以使用以下选项:

    SELECT * 
    FROM table 
    WHERE id >= (SELECT MAX(id) FROM table) - 10 
    ORDER BY id DESC
    

    同样地,20和30个条目也是如此。 这看起来不太清楚,但只要有“id”列的索引,就可以快速工作。