2014-03-01 50 views
0

背景優化工作MYSQL聲明

我有「的用戶」中,「內容」的表,「content_likes」的表的表。當用戶「喜歡」某項內容時,會將關係添加到「content_likes」。簡單。

現在我想要做的是根據它收到的喜好數量來訂購內容。這是相對容易的,但是,我只想一次檢索10個項目,然後使用延遲加載,我正在檢索接下來的10個項目等等。如果select按時間順序排列,則在select語句中執行偏移量很容易,但是,由於「喜歡」數量的排序,我需要另一個列可以抵消。因此,我在結果集中添加了「排名」列,然後在下一次我可以抵消的10個項目的調用中。

這個查詢工作,並做我需要做的。但是,我擔心表現。任何人都可以建議優化此查詢。甚至可能是一個更好的方法。

DB模式

CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 

CREATE TABLE `content` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`owner_id` int(11) NOT NULL, 
`added` int(11) NOT NULL, 
`deleted` int(11) NOT NULL DEFAULT '0', 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 

CREATE TABLE `content_likes` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`content_id` int(11) NOT NULL, 
`user_id` int(11) NOT NULL, 
`added` int(11) NOT NULL, 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 

*欄目爲簡單起見省略

查詢明細

  • 組的content_id在content_likes關係表中,以便通過喜歡DESC
  • 添加一列「r ank「(或行號)結果集並按此排序
  • 加入」內容「表,以便可以忽略具有已刪除標誌的任何內容
  • 僅返回」排名「(或行號)較大的結果比可變
  • 限制結果設置爲10

MySQL的

SELECT 
    results.content_id, results.likes, results.rank 
FROM 
    (
     SELECT 
      t1.content_id, t1.likes, @rn:[email protected]+1 AS rank 
     FROM 
     (
      SELECT 
       cl.content_id, 
       COUNT(cl.content_id) AS likes 
      FROM 
       content_likes cl 
      GROUP BY 
       cl.content_id 
      ORDER BY 
       likes DESC, 
       added DESC 
     ) t1, (SELECT @rn:=0) t2 
     ORDER BY 
      rank ASC 
    ) results 
LEFT JOIN 
    content c 
ON 
    (c.id = results.content_id) 
WHERE 
    c.deleted <> 1 
AND 
    results.rank > :lastRank 
LIMIT 
    10 

MYSQL ALTERNATIVE

SELECT 
    * 
FROM 
(
    SELECT 
     results.*, @rn:[email protected]+1 AS rank 
    FROM 
    (
     SELECT 
      c.id, cl.likes 
     FROM 
      content c 
     INNER JOIN 
      (SELECT content_id, COUNT(content_id) AS likes FROM content_likes GROUP BY content_id ORDER BY likes DESC, added DESC) cl 
     ON 
      c.id = cl.content_id 
     WHERE 
      c.deleted <> 1 
     AND 
      c.added > :timeago 
     LIMIT 
      100 
    ) results, (SELECT @rn:=0) t2 
) final 
WHERE 
    final.rank > :lastRank 
LIMIT 
    5 

「另類」mysql查詢的工作原理,我也喜歡它。內容按用戶的喜好數排序,我可以通過插入最後一個行號來抵消。我在這裏試圖做的是限制結果集,所以如果和當表獲得大的性能不會受到太大的阻礙。在此示例中,僅返回時間範圍內的內容,並且限制爲100。然後我可以抵消行號(延遲加載/分頁)

任何幫助或建議總是讚賞。我比較到mysql一個新手,所以是一種:)

+1

請編輯您的問題以顯示錶格的定義,包括索引。 –

+0

嘿Ollie,完成 –

+0

否 - 請顯示完整的結構,包括引擎,索引定義和基數。併爲該查詢提供解釋計劃(儘管即使沒有看到詳細信息,仍有大量的調整範圍)。 – symcbean

回答

2

可以消除子查詢:

SELECT results.content_id, results.likes, results.rank 
FROM (SELECT cl.content_id, COUNT(cl.content_id) AS likes, @rn:[email protected]+1 AS rank 
     FROMc content_likes cl cross join 
      (SELECT @rn:=0) t2 
     GROUP BY cl.content_id 
     ORDER BY likes DESC, added DESC 
    ) results LEFT JOIN 
    content c 
    ON c.id = results.content_id 
WHERE c.deleted <> 1 AND 
     results.rank > :lastRank 
LIMIT 10; 

不過,我不認爲這會對性能有明顯影響。你可能應該做的是存儲喜歡和「添加」值的最後一個數字,並使用它們來過濾數據。查詢需要一點固定起來,因爲added沒有明確的order by條款中規定:

SELECT results.content_id, results.likes, results.rank, results.added 
FROM (SELECT cl.content_id, COUNT(cl.content_id) AS likes, MAX(added) as added, @rn:[email protected]+1 AS rank 
     FROMc content_likes cl cross join 
      (SELECT @rn := :lastRank) t2 
     WHERE likes < :likes or 
      likes = :likes and added < :added 
     GROUP BY cl.content_id 
     ORDER BY likes DESC, added DESC 
    ) results LEFT JOIN 
    content c 
    ON c.id = results.content_id 
WHERE c.deleted <> 1 AND 
     results.rank > :lastRank 
LIMIT 10; 

這將至少減少需要進行排序的行數。

+0

優秀信息:)謝謝! –

+0

嗨戈登,我添加了一個mysql替代查詢我的問題。你怎麼看? –

+0

@MartinCurrah。 。 。你的選擇看起來很好,但它仍然有一個額外的子查詢是沒有必要的。對於100條記錄,這在性能方面可能不是很大的開銷。 –