2013-01-16 131 views
2

以下MySQL查詢的速度如此之慢?優化緩慢的MySQL查詢

我已經嘗試向DocumentRevision.document和Document.status添加索引,但仍然通常需要永久執行查詢(似乎如果按順序查詢,查詢加速)。是否有另一種更有效的方式來實現相同的結果?查詢獲取數據庫中所有可用文檔的所有最新修訂版本。有兩張桌子。 Document和DocumentRevision。文檔表只包含標識和狀態字段,而DocumentRevision包含所有數據和「文檔」標識字段,以便它知道它是哪個文檔的修訂版本。

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; 
+0

'EXPLAIN'說什麼? –

+0

平均有多少修訂(每個文檔)?是否有很多沒有修改的文檔? – Quassnoi

+0

平均數百。沒有很多文件沒有很多的修訂。實際上目前大約有200多種不同的文件。 – JSour

回答

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)(按此順序)上創建一個複合索引,以便查詢快速運行。

0

看你的需求:

有兩個表。 Document和DocumentRevision。文檔表 只包含id和狀態字段,而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 
+1

是什麼讓你覺得'rev。*'屬於修訂號最大的版本? – Quassnoi

+0

作者在代碼中寫了'MAX(DocumentRevision.id)as maxRevId'。而文檔表只有id和狀態。修訂表具有**所有**修訂版,這意味着它也具有最大修訂版號。 在原始查詢中,只有'Document'表的引用是'where status = 0'子句。和上ID列加入這等於「DocumentRevision.document」 不知道的數據,整個表結構,這似乎爲這一問題的解決最短。 –

+2

問題是,您正在從documentRevision表中獲取各種列,這些列不是聚合列或GROUP BY子句。因此,這些列的值可能屬於該文檔ID的任何修訂版本。 – Kickstart

0

我懷疑有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 to show identical results。請注意沒有子查詢的查詢的更簡單的計劃。

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:[email protected]+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上有索引來優化連接。