表格(InnoDB):
查询:
SELECT S.*
FROM STUDENT S
JOIN CLASS_STUDENT CS ON CS.STUDENT_ID = S.STUDENT_ID
JOIN CLASS C ON C.CLASS_ID = CS.CLASS_ID
WHERE S.ACTIVE = TRUE
GROUP BY S.STUDENT_ID --this suffices to fetch students only once
ORDER BY C.CLASS_DATE DESC --datetime field
LIMIT 0,5
ORDER BY
的执行时间:3.2297秒
没有ORDER BY
的执行时间:0.0015秒
我的系统中有3秒钟会导致用户体验不佳。有没有办法用ORDER BY
加速此查询? LIMIT
用于分页。我是按CLASS_DATE
desc订购的,因为我希望在我的分页结果中首先看到参加最近课程的学生。
我无法删除我正在使用的联接。
谢谢!
编辑:EXPLAIN
两个查询:
EDIT2:innodb_buffer_pool_size
= 4GB,我的服务器中有16 GB
答案 0 :(得分:1)
如果这是我的项目,我只会列出每个学生一次。不是多次,在注册(CLASS_STUDENT
)表格中为每一行重复学生。
我会认真考虑通过向STUDENT
表添加派生列来对数据库实现进行非规范化,例如
ALTER TABLE STUDENT ADD latest_class_date DATE DEFAULT NULL;
填充该栏目:
UPDATE STUDENT t
LEFT
JOIN ( SELECT cs.student_id
, MAX(c.class_date) AS latest_class_date
FROM CLASS_STUDENT cs
JOIN CLASS c
ON c.class_id = cs.class_id
GROUP BY cs.student_id
) s
ON t.student_id = s.student_id
SET t.latest_class_date = s.latest_class_date
添加新列后,我可以创建一个合适的索引,例如
... ON STUDENT (active, latest_class_date, student_id)
然后我的查询更简单:
SELECT s.*
FROM student s
WHERE s.active = 1
ORDER BY s.active DESC, s.latest_class_date DESC, student_id DESC
LIMIT 5
我在student_id
中加入了ORDER BY
,以使结果具有确定性。 (没有它,MySQL可以按任何顺序自由返回任何具有相同latest_class_date
的行。)
我还可以更有效地实现分页,保留上一个先前检索的行中的值,并在查询中提供这些值。获得"接下来的5行":
(编辑:原版中的分页查询模式显然是错误的。这已在此处和后续查询中进行了更正。)
SELECT s.*
FROM student s
WHERE s.active = 1
AND s.latest_class_date <= ?
AND (s.latest_class_date < ? OR s.student_id < ? )
ORDER BY s.active DESC, s.latest_class_date DESC, student_id DESC
LIMIT 5
需要维护新latest_class_date
列的内容。有几个选择。
如果我可以忍受该列内容可能不同步的一段时间,那么
如果我要求保持该列内容的同步,则可以:
修改管理student,class和student_class表的应用程序,以确保在latest_class_date
和CLASS
表中添加/更改/删除行时填充STUDENT_CLASS
列,或
在表格上添加触发器以保持该列的填充
BEFORE INSERT/UPDATE
触发STUDENT
AFTER INSERT/UPDATE/DELETE
触发CLASS_STUDENT
AFTER INSERT/UPDATE/AFTER DELETE
触发CLASS
(我需要记住,外键操作不会触发触发器。例如,如果从{{{} {{}}}中删除行,则从CLASS_STUDENT
删除行作为FOREIGN KEY的CASCADE操作的结果1}},那么只会触发CLASS
表的触发器。这意味着我必须在CLASS
表的删除触发器中处理所需的操作。)
<强>后续强>
如果您需要所有联接&#34;由于这些表格中的其他信息你可能需要一天的时间,我上面提出的建议并没有多大帮助。在性能,甜甜圈的美元方面,&#34;使用filesort&#34;在一个吃午饭的大集合上操作。在我建议的查询中添加联接并不能避免这种情况&#34;使用filesort&#34;操作
如果我建议的查询有合理的性能,那么我们可以将这个查询用作内联视图,以限制返回的行数,然后再进行连接。
但在我们这样做之前,我们必须首先解决当注册(CLASS
)表中有多行时返回的重复学生行。我们希望同一个学生多次返回吗?或者我们想要多次返回学生行,对于CLASS_STUDENT
的每一行,对于具有相同CLASS_STUDENT
的班级,一次。或者,我们是否只想返回学生行一次,只有一个 class_date
的信息?如果我们为学生返回多行,是打算每页列出五个学生,还是每页列出五个student_class?
假设&#34;分页&#34;每页五行,我们期待这样的结果集吗?
第1行至第5行
CLASS
第6行至第10行
student class class_date
------- ----- ----------
Sam phys 2016-02-12
Sam calc 2016-02-12
Mary lit 2016-02-12
Mahatma art 2016-02-12
Paul music 2016-02-11
我们编写的查询将由规范通知。
我们可以写出无数可能的查询。但是如果没有规范,那些查询中的每一个都只是猜测。在不知道返回的结果集的情况下(在各种可能的条件下),我们无法验证我们编写的查询是否正确。
我再次查看了您的查询,并注意到您确实有Paul engl 2016-02-11
Sam art 2016-02-10
...
。 (我们将假设GROUP BY student_id
表中student_id
是唯一的。)
如果学生的students
表格中有多行,并且与CLASS_STUDENT
相关的CLASS
行的class_date
值不同,则class_date
的值在原始查询中返回的是 indeterminate 。 MySQL可以自由选择任何可能的class_date
值。 (它不只是class_date
列......来自CLASS
和CLASS_STUDENT
的行中的值是不确定的。)
使用原始查询,无法保证学生使用&#34;最新的&#34; class_date将在其他学生之前列出。例如,使用此集:
student class_date
------- ----------
Sam 2016-02-22
Sam 2015-07-17
Paul 2016-01-11
上面的查询可以在Sam
之前订Paul
行,在Sam
之后订Paul
行。结果对原始查询有效。并且您无法保证每次运行查询时结果都是相同的。结果是不确定。
其他数据库会在原始查询中引发错误,而SELECT行列表中的非聚合不出现在GROUP BY子句&#34;中。 MySQL特定的扩展允许查询执行。通过在ONLY_FULL_GROUP_BY
中加入sql_mode
,可以让MySQL的行为与其他数据库相同,并抛出错误。
SELECT d.*
FROM ( SELECT s.student_id
, s.latest_class_date
FROM student s
WHERE s.active = 1
AND s.latest_class_date <= ?
AND ( s.latest_class_date < ? OR s.student_id < ? )
ORDER BY s.active DESC, s.latest_class_date DESC, student_id DESC
LIMIT 5
) r
JOIN student d
ON d.student_id = r.student_id
JOIN class_student e
ON e.student_id = d.student_id
JOIN class c
ON c.class_id = e.class_id
AND c.class_date = r.latest_class_date
GROUP BY d.student_id
答案 1 :(得分:0)
尝试在加入学生之前过滤:
SELECT S.*
FROM STUDENT S
JOIN
( select CS.STUDENT_ID, MAX(C.CLASS_DATE) AS maxDate
from CLASS_STUDENT CS
JOIN CLASS C ON C.CLASS_ID = CS.CLASS_ID
GROUP BY CS.STUDENT_ID
ORDER BY maxDate DESC
-- this might include non-active students
-- but hopefully returns at least 5 students with S.ACTIVE = TRUE
LIMIT 0,10
) dt
ON dt.STUDENT_ID = S.STUDENT_ID
WHERE S.ACTIVE = TRUE
ORDER BY dt.maxDate DESC
LIMIT 0,5
如果有很多不活跃的学生(400.000中有35.000)你可能需要增加内部限制,但另一方面,不活跃的学生可能不会参加最近的课程: - )