优化工作的MYSQL语句

时间:2014-03-01 18:22:49

标签: mysql sql

背景

我有一个“用户”表,一个“内容”表和一个“content_likes”表。当用户“喜欢”内容项时,将关系添加到“content_likes”。简单。

现在我要做的是根据收到的喜欢的次数订购内容。这是相对容易的,但是,我只想一次检索10个项目然后使用延迟加载我正在检索接下来的10个项目,依此类推。如果select是按时间排序的,那么在select语句中很容易进行偏移,但是,由于“喜欢”的数量排序,我需要另一列我可以抵消。所以我在结果集中添加了一个“rank”列,然后在下一次调用10个项目时,我可以通过它来抵消。

此查询工作并执行我需要执行的操作。但是,我担心表现。任何人都可以建议优化此查询。甚至可能是一种更好的方法。

DB SCHEMA

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_likes关系表中分组content_id,并按喜欢desc
  • 排序
  • 通过此
  • 向结果集和订单添加列“排名”(或行号)
  • 加入“content”表,以便可以省略任何带有已删除标记的内容
  • 仅返回“rank”(或行号)大于变量
  • 的结果
  • 将结果限制为10

MYSQL

SELECT 
    results.content_id, results.likes, results.rank
FROM
    (
        SELECT 
            t1.content_id, t1.likes, @rn:=@rn+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替代

SELECT 
    *
FROM
(
    SELECT
        results.*, @rn:=@rn+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

“Alternative”mysql查询也可以像我一样工作。内容按用户的喜欢次数排序,我可以通过插入最后一行编号来抵消。我试图在这里做的是限制结果集,以便在表格获得大的性能时不会受到太大影响。在此示例中,仅返回时间范围内的内容,并且将返回限制为100。然后我可以通过行号(延迟加载/分页)

来抵消

任何帮助或建议总是受到赞赏。我是mysql的新手,所以要善良:)

1 个答案:

答案 0 :(得分:2)

您可以删除子查询:

SELECT results.content_id, results.likes, results.rank
FROM (SELECT cl.content_id, COUNT(cl.content_id) AS likes, @rn:=@rn+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:=@rn+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;

这至少会减少需要排序的行数。