[英]Optimizing slow MySQL query
在以下MySQL查询中,什么是如此缓慢?
我曾尝试向DocumentRevision.document和Document.status添加索引,但是仍然通常需要永久执行查询(看来,如果按顺序进行此查询,则查询会加快速度)。 是否有另一种更有效的方法来达到相同的结果? 该查询将获取数据库中所有可用文档的所有最新修订。 有两个表。 文档和文档修订。 文档表仅包含id和status字段,而DocumentRevision包含所有数据以及“ document” id字段,以便它知道它是哪个文档的修订版。
SELECT rev.document as documentId, rev.id as revId, rev.name as name,
rev.dateCreated as dateCreated, rev.documentOrder as documentOrder
FROM (
SELECT Document.id as docId, MAX(DocumentRevision.id) as maxRevId
FROM Document, DocumentRevision
WHERE Document.id = DocumentRevision.document AND Document.status = 0
GROUP BY Document.id
) AS x
INNER JOIN DocumentRevision as rev on rev.document = x.docId
AND rev.id = x.maxRevId
ORDER BY dateCreated DESC;
您有两种选择:
SELECT dr.*
FROM (
SELECT document, MAX(id) AS maxid
FROM documentRevision
GROUP BY
document
) drd
JOIN documentRevision dr
ON dr.id = drd.maxid
JOIN document d
ON (d.id, d.status) = (drd.document, 0)
和
SELECT dr.*
FROM document d
JOIN documentRevision dr
ON dr.id =
(
SELECT id
FROM documentRevision dri
WHERE dri.document = d.id
ORDER BY
document DESC, id DESC
LIMIT 1
)
除非您每个文档确实有很多修订,否则第一个可能最有效率。
在documentRevision (document, id)
(按此顺序)创建一个复合索引,以使查询快速运行。
查看您的要求:
有两个表。 文档和文档修订。 文档表仅包含id和status字段,而DocumentRevision包含所有数据以及“ document” id字段,以便它知道它是哪个文档的修订版。
和您的代码一样,我编写了另一个没有子选择的查询。 仅对document.ID和documentRevision.document进行一个选择和适当的索引,这样才能更有效,从而具有良好的联接性能。
SELECT rev.document docID, MAX(rev.id) revID, rev.name revName, rev.dateCreated dateCreated, rev.documentOrder docOrder
FROM DocumentRevisin as rev, Document as doc
WHERE doc.status = 0 AND doc.id = rev.document
GROUP BY rev.document, rev.name revName, rev.dateCreated dateCreated, rev.documentOrder docOrder
我怀疑如果结果很大,使用GROUP BY
子查询可能会导致一些执行计划问题; 您可能想尝试不带子查询的查询。
(在Document.id
, DocumentRevision.document
和DocumentRevision.status
上的索引会有所帮助。)
SELECT rev.document as documentId, rev.id as revId, rev.name as name,
rev.dateCreated as dateCreated, rev.documentOrder as documentOrder
FROM Document doc
JOIN DocumentRevision rev
ON doc.id=rev.document
LEFT JOIN DocumentRevision rev2
ON rev.document = rev2.document AND rev.id < rev2.id
WHERE doc.status=0 AND rev2.id IS NULL
ORDER BY dateCreated DESC;
SQLfiddle显示相同的结果 。 请注意没有子查询的更简单的查询计划。
您的查询多次使用了join中的documentRevision表。 当然还有优化的空间。
在其他DBMS中(例如Teradata或MS SQL Server),这可以通过sum(1) over(partition by rev.document order by rev.id desc)
形式的“窗口聚合”功能来完成。
MySQL没有窗口聚合功能。 但是使用参数也可以这样做:
select * from (
select
if(@doc_id_grp=rev.document,@rank:=@rank+1,@rank:=1) rank /*the same document.id (documentRevision.document) is considered the same group, in the group, @rank increntally increases, when the doc_id changes, @rank resets to 0*/
,@doc_id_grp:=rev.document as doc_id
,rev.id as rev_id
,rev.name as name
,rev.datecreated as datecreated
,rev.documentorder as documentorder
from Document doc
join DocumentRevision rev
on doc.id=rev.Document
,(select @rank:=0,@doc_id_grp:=0) a
order by rev.document,rev.id desc
where doc.status=0
) x
where rank=1
这样,DBMS不会将表联接两次,而只会联接一次然后进行排序。
我没有MySQL环境可以对此进行测试,但是可以根据需要进行调整。 我希望这会有所帮助。 请同时在Document.id和DocumentRevision.document上建立索引以优化连接。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.