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
子句。
请注意:执行此类操作的最有效方法通常是使用变量来枚举行。
我正在运行 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
子句。
请注意:执行此类操作的最有效方法通常是使用变量来枚举行。