无法优化查询
Posted
技术标签:
【中文标题】无法优化查询【英文标题】:Not able to optimize query 【发布时间】:2018-02-22 17:51:38 【问题描述】:SELECT MIN(classification) AS classification
,MIN(START) AS START
,MAX(next_start) AS END
,SUM(duration) AS seconds
FROM ( SELECT *
, CASE WHEN (duration < 20*60) THEN CASE WHEN (duration = -1) THEN 'current_session' ELSE 'session' END
ELSE 'break'
END AS classification
, CASE WHEN (duration > 20*60) THEN ((@sum_grouping := @sum_grouping +2)-1)
ELSE @sum_grouping
END AS sum_grouping
FROM ( SELECT *
, CASE WHEN next_start IS NOT NULL THEN TIMESTAMPDIFF(SECOND, START, next_start) ELSE -1 END AS duration
FROM ( SELECT id, studentId, START
, (SELECT MIN(START)
FROM attempt AS sub
WHERE sub.studentId = main.studentId
AND sub.start > main.start
) AS next_start
FROM attempt AS main
WHERE main.studentId = 605
ORDER BY START
) AS t1
) AS t2
WHERE duration != 0
) AS t3
GROUP BY sum_grouping
ORDER BY START DESC, END DESC
解释和目标
attempt
表记录了学生在课程期间尝试进行的某些活动。如果两次尝试相隔不到 20 分钟,我们认为它们是同一个会话。如果他们相隔超过 20 分钟,我们假设他们休息了。
我使用此查询的目标是获取所有尝试并将它们浓缩在一个会话和休息列表中,其中包含每个会话的开始时间、结束时间(定义为后续会话的开始时间),以及会议多长时间。 classification
是会话、休息还是当前会话。
上面的查询完成了所有这些,但是太慢了。如何提高性能?
当前查询的工作原理
最里面的查询选择一次尝试的开始时间和后续尝试的开始时间,以及这些值之间的持续时间。
然后,@sum_grouping
和 sum_grouping
用于将尝试拆分为会话和休息时间。 @sum_grouping
仅在尝试超过 20 分钟(即休息)时才会增加,并且始终增加 2。但是,sum_grouping
设置为比“休息”的值小 1 .如果尝试的时间少于 20 分钟,则使用当前的 @sum_grouping
值,无需修改。因此,所有的休息时间都是不同的奇数值,并且所有会话(无论是 1 次还是多次尝试)最终都是不同的偶数。这允许GROUP BY
部分正确地将尝试分为会话和中断。
例子:
Attempt type @sum_grouping sum_grouping
non-break 0 0
non-break 0 0
break 2 1
break 4 3
non-break 4 4
break 6 5
如您所见,所有休息时间都将按 sum_grouping
单独分组,具有不同的奇数值,所有非休息时间将分组为具有偶数值的会话。
MIN(classification)
只是在分组行中同时存在“会话”和“当前会话”时强制返回“当前会话”。
SHOW CREATE TABLE attempt
的输出
CREATE TABLE attempt (
id int(11) NOT NULL AUTO_INCREMENT,
caseId int(11) NOT NULL DEFAULT '0',
eventId int(11) NOT NULL DEFAULT '0',
studentId int(11) NOT NULL DEFAULT '0',
activeUuid char(36) NOT NULL,
start timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
end timestamp NULL DEFAULT NULL,
outcome float DEFAULT NULL,
response varchar(5000) NOT NULL DEFAULT '',
PRIMARY KEY id),
KEY activeUuid activeUuid),
KEY caseId caseId,activeUuid),
KEY end end),
KEY start start),
KEY studentId studentId),
KEY attempt_idx_studentid_stat_id studentId,start,id),
KEY attempt_idx_studentid_stat studentId,start
) ENGINE=MyISAM AUTO_INCREMENT=298382 DEFAULT CHARSET=latin1
【问题讨论】:
背景是什么?解释设置,哪些列带有索引?您对优化有什么想法?为什么你认为当前的查询不好? 并添加表格结构、样本数据、你得到的样本输出、你想要得到的样本输出。 @luksch 当前查询需要很长时间才能执行。 @suri 将SHOW CREATE TABLE attempt
的输出添加到您的问题中。
@WillemRenzema 请找到输出:
【参考方案1】:
(这不是一个正确的答案,但无论如何都是这样。)
尽量不要嵌套“派生”表。
我看到很多语法错误。
从 MyISAM 迁移到 InnoDB。
INDEX(a, b)
处理您需要INDEX(a)
的情况,所以DROP
是后者。
【讨论】:
也应该采用utf8mb4而不是latin1 但不适用于 UUID。 @RickJames 我已经为 OP 的问题添加了一个解释,可以让您提供更完整的答案。我对用户定义的变量没有经验,所以我希望像你这样更熟练的人,或者 BillKarwin 或 ypercube 或 Rolando 等,可能能够提供帮助。 @WillemRenzema - 我可以设想如何发现休息时间,但这应该通过“会话”和“休息”来完成?让我们看看示例输出。我希望输出中有“start-break”和“end-break”时间戳??以上是关于无法优化查询的主要内容,如果未能解决你的问题,请参考以下文章