繁体   English   中英

优化慢速MySQL查询

[英]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.idDocumentRevision.documentDocumentRevision.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM