提高从历史表中检索最后状态的性能
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 by
和limit
:
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) 上的索引也会有所帮助
【讨论】:
以上是关于提高从历史表中检索最后状态的性能的主要内容,如果未能解决你的问题,请参考以下文章
将检索到的对象推送到 React 中的状态只会给我带来最后一个