如何解决在MariaDB中,如何选择每天的最后一个事件并为其创建索引?
我有一个会议表,每个会议都有一个开始和结束时间,表示为一个整数 Unix 时间戳,以及一组参加这次会议的人。一旦他们当天的所有会议结束,我需要向每组人发送通知。为此,我需要为(天、组)的每个组合找到最后一次会议。我已经根据我的情况调整了 this answer,并且有效:
SELECT MAX(`starts_at`),`id`,`group_id`,DATE(FROM_UNIXTIME(`starts_at`)) `day`
FROM `meeting`
GROUP BY `day`,`group_id`;
然而,尽管尝试了不同的索引组合,但我似乎无法找到使此查询不执行全表扫描的组合。 EXPLAIN
结果始终如下:
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
| 1 | SIMPLE | meeting | ALL | NULL | NULL | NULL | NULL | 16 | Using temporary; Using filesort |
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
我的表是这样定义的:
CREATE TABLE `meeting` (
`id` int(11) NOT NULL AUTO_INCREMENT,`group_id` int(11) NOT NULL,`starts_at` int(11) NOT NULL,`ends_at` int(11) NOT NULL,... other fields ...,PRIMARY KEY (`id`),CONSTRAINT `meeting_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `group` (`id`),)
我需要什么索引组合和什么查询?我看到的解决方案之一是创建一个索引列来存储事件的日期,也许作为某种序数,虽然这是一个选项,但我希望尽可能避免它,以便在单个行。
解决方法
如果您运行的是 MySQL 8.0.13 或更高版本,您可以尝试在表达式上建立索引:
create index idx_meeting on meeting(
group_id,(date(from_unixtime(starts_at))),starts_at desc
);
这首先放置出现在group by
子句中的列/表达式,然后是聚合的列;由于我们将查找 max()
,因此我们希望在索引中按降序对该列进行排序。
在this DB Fiddle中,该索引似乎是由数据库提取的(但请注意,我没有可使用的数据 - 结果在您的环境中可能会有所不同:
id | select_type | 表格 | 分区 | 输入 | possible_keys | key | key_len | ref | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | 会议 | null | 索引 | idx_meeting | idx_meeting | 12 | null | 1 | 100.00 | 使用索引;使用临时 |
进一步的选择是稍微简化查询。我们可以算术而不是时间戳转换。我们可以轻松地在其上放置索引 - 如果您的版本不支持表达式索引,则可以使用计算列:
alter table meeting
add starts_date date as (floor(starts_at / 60 / 60 / 24))
stored;
create index idx_meeting2 on meeting(group_id,starts_date,starts_at desc);
然后我们按如下方式运行查询:
SELECT MAX(starts_at),group_id,DATE(MAX(starts_at)) day
FROM meeting
GROUP BY group_id,starts_day;
id | select_type | 表格 | 输入 | possible_keys | key | key_len | ref | 行 | 额外 |
---|---|---|---|---|---|---|---|---|---|
1 | 简单 | 会议 | 索引 | null | idx_meeting2 | 12 null | 1 | 使用索引 |
编辑:您需要每个组和每天的最新事件。聚合是不合适的。相反,我们需要过滤。
考虑以下设置:
alter table meeting
add starts_day int(11) as (floor(starts_at / 60 / 60 / 24) * 60 * 60 * 24)
stored;
create index idx_meeting on meeting(group_id,starts_day,starts_at desc);
现在我们可以使用以下查询:
select starts_at,date(from_unixtime(starts_day)) day
from meeting m
where starts_at = (
select max(m1.starts_at)
from meeting m1
where m1.group_id = m.group_id and m1.starts_day = m.starts_day
)
子查询利用索引,也可以部分用于外部查询。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。