使用 Postgresql 进行高效的最新记录查询

Posted

技术标签:

【中文标题】使用 Postgresql 进行高效的最新记录查询【英文标题】:Efficient latest record query with Postgresql 【发布时间】:2010-12-13 15:27:32 【问题描述】:

我需要做一个大查询,但我只想要最新的记录。

对于单个条目,我可能会做类似的事情

SELECT * FROM table WHERE id = ? ORDER BY date DESC LIMIT 1;

但我需要为大量(数千条条目)记录提取最新记录,但只提取最新条目。

这就是我所拥有的。这不是很有效。我想知道是否有更好的方法。

SELECT * FROM table a WHERE ID IN $LIST AND date = (SELECT max(date) FROM table b WHERE b.id = a.id);

【问题讨论】:

那么,我的SELECT DISTINCT 查询对您有什么帮助吗?它应该比相关子选择快,但我不确定要快多少。 使用这个***.com/a/2111420/454769 【参考方案1】:

如果您不想更改数据模型,可以使用DISTINCT ON 为“a”中的每个条目从表“b”中获取最新记录:

SELECT DISTINCT ON (a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY a.id, b.date DESC

如果您想避免在查询中“排序”,添加这样的索引可能对您有所帮助,但我不确定:

CREATE INDEX b_id_date ON b (id, date DESC)

SELECT DISTINCT ON (b.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY b.id, b.date DESC

或者,如果您想以某种方式对表“a”中的记录进行排序:

SELECT DISTINCT ON (sort_column, a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY sort_column, a.id, b.date DESC

替代方法

但是,上述所有查询仍然需要从表“b”中读取所有引用的行,因此如果您有大量数据,它可能仍然太慢了。

您可以创建一个新表,该表仅保存每个 a.id 的最新“b”记录,甚至可以将这些列移动到“a”表本身。

【讨论】:

如果您正在寻找更有效的解决方案,请尝试以下 Manji 的答案。它的基准测试速度比使用此处介绍的 DISTINCT ON 解决方案快约 3 倍。 这家伙全力以赴:***.com/a/7630564/1699320【参考方案2】:

这可能更有效。区别:对表 b 的查询仅执行 1 次,您的相关子查询针对每一行执行:

SELECT * 
FROM table a 
JOIN (SELECT ID, max(date) maxDate
        FROM table
      GROUP BY ID) b
ON a.ID = b.ID AND a.date = b.maxDate
WHERE ID IN $LIST 

【讨论】:

看起来很有希望,但连接确实效率低下。 为什么你认为连接效率低下,尤其是考虑到它只连接一行? 在测试了这两种方法后,max(date) 对我来说比 DISTINCT ON 快了大约 3 倍。 这真是一个很棒的解决方案!非常感谢!我的查询时间从 470 毫秒减少到 95 毫秒。我使用 max(id) 作为最后一行标识符。所以它可能比日期时间比较更有效。 在我的例子中,user 表包含系统的所有用户,invitation 表包含发送成为用户的所有邀请。每个电子邮件地址可以发送多个邀请,每个邀请都有一个valid_until 日期、一个invitation_accepted 标志和一个created_date。我们只关心最近的邀请。如果您接受在所有列上使用聚合函数,则性能使其成为最佳答案。【参考方案3】:

您对此有何看法?

select * from (
   SELECT a.*, row_number() over (partition by a.id order by date desc) r 
   FROM table a where ID IN $LIST 
)
WHERE r=1

我过去经常使用它

【讨论】:

【参考方案4】:

On 方法 - 创建一个小型衍生表,其中包含表 a 上的最新更新/插入时间 - 将此表称为 a_latest。表 a_latest 需要足够的粒度来满足您的特定查询要求。在您的情况下,使用应该就足够了

CREATE TABLE 
a_latest 
( id INTEGER NOT NULL, 
  date TSTAMP NOT NULL, 
  PRIMARY KEY (id, max_time) );

然后使用类似于 najmeddine 建议的查询:

SELECT a.* 
FROM TABLE a, TABLE a_latest 
USING ( id, date );

那么诀窍就是让 a_latest 保持最新。使用插入和更新触发器来执行此操作。用 plppgsql 编写的触发器相当容易编写。如果您愿意,我很乐意提供一个示例。

这里的重点是,最新更新时间的计算是在更新本身期间进行的。这将更多的负载从查询转移。

【讨论】:

【参考方案5】:

如果每个 id 有很多行,你肯定需要一个相关的子查询。 它会为每个 id 进行 1 个索引查找,但这比对整个表进行排序要快。

类似:

SELECT a.id,
(SELECT max(t.date) FROM table t WHERE t.id = a.id) AS lastdate
FROM table2;

您将使用的“table2”不是您在上面的查询中提到的表,因为在这里您需要一个不同 id 的列表以获得良好的性能。由于您的 id 可能是另一个表的 FK,因此请使用这个。

【讨论】:

以上是关于使用 Postgresql 进行高效的最新记录查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在 postgresql 中进行查询,以查找所有启动时间超过 6 小时的记录?

如何在 postgresql 中选择最新数据

Postgresql查询因添加WHERE约束而无法解释

CitusDB —— 基于最新 PostgreSQL 构建的分布式数据库

PostgreSQL函数:查询包含时间分区字段的表,并更新dt分区为最新分区

如何使用新的 PostgreSQL JSON 数据类型中的字段进行查询?