提高从历史表中检索最后状态的性能

Posted

技术标签:

【中文标题】提高从历史表中检索最后状态的性能【英文标题】:Improve performance of a last status retrieval from history table 【发布时间】:2021-04-02 19:39:13 【问题描述】:

我想从历史记录表中检索项目的最新状态。历史表将记录一个项目的所有状态变化。查询必须快速运行。

以下是我用来获取每个项目的最新状态的查询

    SELECT item_history.*
    FROM item_history
    INNER JOIN (
      SELECT MAX(created_at) as created_at, item_id
      FROM item_history
      GROUP BY item_id
    ) as latest_status
      on latest_status.item_id = item_history.item_id 
        and latest_status.created_at = item_history.created_at
  WHERE item_history.status_id = 1
    and item_history.created_at BETWEEN "2020-12-16" AND "2020-12-23"

我已经尝试将上面的查询放入另一个内部联接中以将数据与项目链接:

SELECT *
FROM `items`
INNER JOIN ( [query from above] )
WHERE items.category_id = 3

备注关于 item_history 表,我在以下列上有索引:status_id、creatd_at 和 Listing_id。我还把其中的 3 个变成了复合主键。

我的问题是 mysql 一直在扫描整个表以获取 MAX(created_at),这是一个非常慢的操作,即使我在历史表中只有 300 万条记录。

查询计划如下:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY items NULL ref "PRIMARY,district" district 18 const 694 100.00 NULL
1 PRIMARY item_history NULL ref "PRIMARY,status_id,created_at,item_history_item_id_index" PRIMARY 9 "main.items.id,const" 1 100.00 "Using where"
1 PRIMARY NULL ref <auto_key0> <auto_key0> 14 "func,main.items.id" 10 100.00 "Using where; Using index"
2 DERIVED item_history NULL range "PRIMARY,status_id,created_at,item_history_item_id_index" item_history_item_id_index 8 NULL 2751323 100.00 "Using index"

【问题讨论】:

最佳“groupwise-max”代码在这里:mysql.rjweb.org/doc.php/groupwise_max @RickJames 你一如既往的棒!感谢您撰写所有这些文章 - 它们非常有用。 这是一个有趣的挑战。 【参考方案1】:

我想从历史记录表中检索项目的最新状态。

如果您只需要一项的结果,请使用order bylimit

select *
from item_history
where item_id = ? and created_at between '2020-12-16' and '2020-12-23'
order by created_at desc limit 1

此查询将使(item_id, created_at) 上的索引受益。

如果您想要每个项目的最新状态,我会推荐一个相关子查询:

select *
from item_history h
where created_at = (
    select max(h1.created_at) 
    from item_history h1
    where h1.item_id = h.item_id
      and h1.created_at between '2020-12-16' and '2020-12-23'
)

相同的索引应该是有益的。

【讨论】:

【参考方案2】:

使用窗口函数 MySQL 8.0.14+:

WITH cte AS (
  SELECT *, ROW_NUMBER() OVER(PARTITION BY item_id ORDER BY created_at DESC) r
  FROM item_history
  WHERE item_history.status_id = 1
    and item_history.created_at BETWEEN '2020-12-16' AND '2020-12-23'
)
SELECT *
FROM cte WHERE r = 1;

(item_id,created_at) 上的索引也会有所帮助

【讨论】:

以上是关于提高从历史表中检索最后状态的性能的主要内容,如果未能解决你的问题,请参考以下文章

从状态更新表中有效获取具有给定状态的ID

获取表中的最后一项 - SQL

将检索到的对象推送到 React 中的状态只会给我带来最后一个

如果 PlaySound() 失败,是不是可以检索最后的错误状态?

SQL如何从每个连接表中检索最新结果

用于提高状态行性能的 Powershell 作业