选择五个最高的不同记录

时间:2015-01-06 21:14:34

标签: mysql sql

我正在运行MySQL服务器。在我的数据库中,我有一个用于记录页面请求的表:

CREATE TABLE IF NOT EXISTS `log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` smallint(5) NOT NULL,
  `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `page` varchar(127) NOT NULL
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

我想获取日志列表,这样只显示访问某个页面的任何用户的最后五个记录。例如,如果某个用户已访问某个页面七次,则仅显示最后五个记录。除此之外,查询应返回所有记录。

我提出了以下问题:

SELECT * 
FROM `log` 
WHERE `time` > 
  (SELECT `time` FROM `log` as l 
  WHERE `l`.`userId`=`log`.`userId` AND `l`.`page`=`log`.`page` 
  ORDER BY `time` DESC LIMIT 5,1)
ORDER BY `time` DESC

子查询应该选择第五条记录。这很好用。但是,当用户没有访问某个页面超过五次时,不会返回任何内容,因为子查询无法找到结果(我认为)。我怎样才能使它适用于那种情况呢?

2 个答案:

答案 0 :(得分:1)

首先,我刚学到了一些东西。 MySQL在limit的子查询中不允许in,我认为这扩展到了其他类似的操作(参见documentation)。但是,显然这是有效的。

注意:以下两项工作均不适用。

此版本可能有效:

WHERE `time` > (SELECT MIN(`time`)
                FROM (SELECT time
                      FROM `log` l 
                      WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
                      ORDER BY `time` DESC
                      LIMIT 5
                     ) l
               )

您也可以尝试这种表述:

WHERE `time` > ANY (SELECT time
                    FROM `log` l 
                    WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
                    ORDER BY `time` DESC
                    LIMIT 5
                   )

我不确定子查询中对LIMIT的限制是否适用于ANY关键字。

编辑:

由于MySQL的限制,上述两种方法均无效。假设没有太多匹配,以下内容应该有效:

WHERE `time` > (SELECT TIME(SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(time ORDER BY time DESC), ',', 5), ',' -1))
                FROM `log` l 
                WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
               )

这会使用group_concat() / substring_index()技巧。我不喜欢这个解决方案,但它在技术上应该可以工作,除非你在一个组中有这么多匹配超过group_concat()的长度限制(总是可以设置为更高的值)。我确实想知道是否有更好的方法,只使用where子句。

作为注释:执行此类操作的最有效方法通常是使用变量来枚举行。

答案 1 :(得分:0)

LIMIT 5,1

表示:

跳过5条记录,然后取1

因此,如果记录少于6条,则不会返回任何结果。