2

以下 MySQL 查询有什么地方这么慢?

我尝试向 DocumentRevision.document 和 Document.status 添加索引,但执行查询通常需要很长时间(似乎如果按顺序进行此查询,查询速度会加快)。是否有另一种更有效的方法来实现相同的结果?该查询获取数据库中所有可用文档的所有最新版本。有两张桌子。文档和文档修订。文档表只包含 id 和 status 字段,而 DocumentRevision 包含所有数据和“文档” 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;
4

4 回答 4

0

你有两个选择:

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)以使查询快速工作。

于 2013-01-16T08:39:27.113 回答
0

GROUP BY如果结果很大,我怀疑带有 a 的子查询可能会导致一些执行计划问题;您可能想尝试不使用子查询的查询。

(上的索引Document.idDocumentRevision.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 显示相同的结果。请注意没有子查询的更简单的查询计划。

于 2013-01-16T09:01:40.083 回答
0

您的查询不止一次在联接中使用了 documentRevision 表。肯定有优化的空间。

在其他 DBMS(例如 Teradata 或 MS SQL 服务器)中,这可以通过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 上有索引以优化连接。

于 2013-01-16T09:12:37.233 回答
0

查看您的要求:

有两张桌子。文档和文档修订。文档表只包含 id 和 status 字段,而 DocumentRevision 包含所有数据和“文档” 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
于 2013-01-16T08:45:04.213 回答