【问题标题】:pagination and filtering on a very large table in postgresql (keyset pagination?)在 postgresql 中对一个非常大的表进行分页和过滤(键集分页?)
【发布时间】:2020-04-24 11:03:03
【问题描述】:

我有一个科学数据库,目前有 4,300,000 条记录。这是一个科学数据库,API 正在为它提供数据。到 2020 年 6 月,我可能会有大约 100,000,000 条记录。

这是表格“输出”的布局:

ID   | sensor_ID    | speed    | velocity | direction
-----------------------------------------------------
1    | 1            | 10       | 1        | up
2    | 2            | 12       | 2        | up
3    | 2            | 11.5     | 1.5      | down
4    | 1            | 9.5      | 0.8      | down
5    | 3            | 11       | 0.75     | up
...

顺便说一句,这是虚拟数据。但输出是一个包含 5 列的表格:ID、sensor_ID、速度、速度和方向。

我想要实现的是一个体面的分页和过滤方法。我想创建一个网站(在 nodejs 中),其中将显示 +4,000,000 条记录(目前),每页 10,000 条记录。我还希望能够过滤 sensor_ID、速度、速度或方向。

目前,我有这个用于选择特定行的查询:

SELECT * FROM output ORDER BY ID DESC OFFSET 0 LIMIT 10000 // first 10,000 rows

SELECT * FROM output ORDER BY ID DESC OFFSET 10000 LIMIT 10000 // next 10,000 rows

...

我正在寻找一些关于创建一个体面的分页方法的信息/提示。 目前,我这样做的速度仍然很快,但我认为当我们达到 +50,000,000 条记录时它会慢很多。

首先,我找到了这个页面:https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/。我对 keyset 分页感兴趣。但老实说,我不知道如何开始。

认为我必须做什么:

在 ID 字段上创建索引:

CREATE UNIQUE INDEX index_id ON output USING btree (ID)

我还找到了这个页面:https://leopard.in.ua/2014/10/11/postgresql-paginattion。当您向下滚动到“改进#2:Seek 方法”时,您可以看到他们删除了 OFFSET 子句,并使用了 WHERE 子句。我还看到他们在查询中使用了 last insert ID

SELECT * FROM output WHERE ID < <last_insert_id_here> ORDER BY ID DESC LIMIT 10000

我不完全理解这一点。对于第一页,我需要最后一个插入 ID。然后我获取 10,000 条最新记录。但在那之后,要获得第二页,我不需要最后一个插入 ID,我需要第 10,000 个最后一个插入 ID(我猜)。

谁能给我一个关于分页和快速过滤的好解释。

我正在使用的东西: - PostgreSQL - pgadmin(用于数据库管理) - node.js(最新版本)

谢谢大家!祝您 2020 年愉快!

编辑 1: 我不知道,但是 massJS (https://massivejs.org/) 可以很好用吗?我应该在所有查询上使用它,还是只在分页查询上使用它?

编辑 2: 我想我已经弄明白了一点(如果我错了,请纠正我)。

假设我有 100,000 条记录:

1) 获取最后插入的 ID

2) 使用最后插入的 ID 获取最后 10,000 条记录

SELECT * FROM output WHERE ID < 100000 ORDER BY ID DESC LIMIT 10000 // last insert ID is here 100,000 because I have 100,000 records

3) 显示 10,000 条记录,同时保存这 10,000 条记录的插入 ID 以用于下一个查询

4) 使用新的最后插入 id 获取接下来的 10,000 条记录

SELECT * FROM output WHERE ID < 90000 ORDER BY ID DESC LIMIT 10000 // 90,000 is the very last insert id - 10,000

5) ...

这是正确的吗?

【问题讨论】:

    标签: sql node.js postgresql


    【解决方案1】:

    我是这样处理的。对于我获取的第一页,我使用

    SELECT id, col, col, col 
      FROM output 
     ORDER BY id DESC
     LIMIT 10000
    

    然后,在我的客户端程序 (node.js) 中,我从结果集的最后一行捕获 id 值。当我需要下一页时,我会这样做。

     SELECT id, col, col, col
       FROM output
      WHERE id < my_captured_id_value
      ORDER BY id DESC
    

    这会利用索引。即使您从表中删除了一些行,它也能正常工作。

    顺便说一句,如果您的第一个分页页面具有最大的 id,您可能需要一个降序索引。 CREATE UNIQUE INDEX index_id ON output USING btree (ID DESC).

    专业提示 SELECT * 对大型数据库的性能有害。始终列出您实际需要的列。

    【讨论】:

    • 谢谢。我正在使用“无限滚动”,但我需要一个“有下一页” - 变量来知道在获取 n 个结果之后是否还有更多结果要获取。如果没有,加载图标必须隐藏。所以当我有 100,000 条记录,并且我获取前 10,000 条记录时,我需要知道是否还有任何记录(实际上是 90,000 条记录),但是在我获取最后 10,000 条记录时的第 10 页,没有任何记录.我怎样才能做到这一点?
    • 我遇到的问题是最后一个查询(当数据库中没有结果时),查询继续运行。假设我有 100 条记录,我一次获取 10 条记录。最后一个查询的最后一个 ID 将为 1。但没有 ID 为 0 的记录,因此 ID 1 = 最后/第一条记录。问题是我的查询将搜索 ID
    • 您说结果集中没有行的查询永远不会完成运行?这太离奇了!查看实际查询?在交互式 shell 中运行它?
    • 我的解释不正确。没有完成就没有运行,但是因为我没有错误“管理”,所以我从来没有看到错误,而且它似乎还在运行。
    【解决方案2】:

    在键集分页中,您应该在要在 ORDER BY 子句中设置的内容上设置 WHERE 子句,而对于 DESC,您应该使用 &lt; ,反之亦然。 对于第一页,您可以使用如下内容:

    SELECT Col1, Col2, Col3 
    FROM db.tbl 
    WHERE Col3 LIKE '%search_term%'
    ORDER BY Col1 DESC , Col2 ASC
    LIMIT 10000
    

    对于下一页,您应该将结果最后一行的 Col1Col2 的值发送到查询,如下所示:

    SELECT Col1, Col2, Col3 
    FROM db.tbl 
    WHERE Col3 LIKE '%search_term%'
    AND ( Col1 < Col1_last_row_value AND Col2 > Col2_last_row_value)
    ORDER BY Col1 DESC , Col2 ASC
    LIMIT 10000
    

    在服务器或客户端,您应该检查您的查询是否带回任何结果,如果没有,则表示您已完成并且必须隐藏“无限滚动”的加载图标

    【讨论】:

      猜你喜欢
      • 2010-09-25
      • 1970-01-01
      • 2021-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-17
      • 1970-01-01
      • 2011-03-26
      相关资源
      最近更新 更多