Select 五个最高的不同记录

Select the five highest distinct records

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

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 ;

我想获取一个日志列表,以便只显示访问特定页面的任何用户的最后五条记录。例如,如果某个用户访问某个页面七次,则只显示最后五条记录。除此之外,查询应该 return 所有记录。

我提出了以下查询:

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

子查询应该select第五条记录。这很好用。但是,当用户访问某个页面的次数不超过五次时,return什么也没有,因为子查询找不到结果(我认为)。我怎样才能让它也适用于这种情况?

LIMIT 5,1

表示:

跳过5条记录,然后取1条

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

首先,我刚刚学到了一些东西。 MySQL 不允许在带有 in 的子查询中使用 limit,我认为这可以扩展到其他类似的操作(请参阅 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 子句。

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