昨晚在某个群上看到一个人抛出一个问题:
我想出两种解法,一种是sql加代码。一种是一句sql搞定。
方法一
首先模拟两张表:
CREATE TABLE `read_log_a`
( `id` int(11) NOT NULL AUTO_INCREMENT,
`article` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '文章',
`read_num` int(11) DEFAULT '0' COMMENT '阅读数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `read_log_b`
( `id` int(11) NOT NULL AUTO_INCREMENT,
`article` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '文章',
`read_num` int(11) DEFAULT '0' COMMENT '阅读数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
执行脚本生成测试数据(需要分别执行两次)两张表分别生成100条数据,阅读数在0到10000之间,便于对比。
/**
* 生成测试数据
*/
public function buildData()
{
$book_name = ['星华字典','收敛之道','羽毛球从菜鸟到省队','酒席文化剖析','面试指南','大话NBA'];
for ($i=0;$i<100;$i++) {
//从一维数组获取随机值。array_rand()返回的是数组随机下标
$name = $book_name[array_rand($book_name)];
$rand_num = rand(0,10000);
$data = [
'read_num' => $rand_num,
// 'article' => $name . '_a_' . $i
'article' => $name . '_b_' . $i
];
// $res = Db::name("read_log_a")->insert($data);
$res = Db::name("read_log_b")->insert($data);
}
dump("插入结束");
}
获取前n名的排行榜方法:
/**
* 获取前n名的排行榜
* @param $rank_limit 指定第n名
*/
public function getReadRank()
{
//获取排行榜的显示长度。默认取10个
$rank_limit = input("rank_limit",10);
//查找两张表阅读数最大的n条记录
$a_sql = "SELECT * FROM `read_log_a` ORDER BY read_num DESC limit {$rank_limit}";
$b_sql = "SELECT * FROM `read_log_b` ORDER BY read_num DESC limit {$rank_limit}";
$a_top_rank = Db::query($a_sql);
$b_top_rank = Db::query($b_sql);
$len = count($a_top_rank) + count($b_top_rank);
//分别取a队列和队列b的尾部不断对比,把小的丢弃。
//循环终止的条件是两个队列的数量和为指定前n个排行版.代表已经取到二者的前n个
while ($len > $rank_limit) {
$a_last_num = end($a_top_rank);
$b_last_num = end($b_top_rank);
if ($a_last_num['read_num'] >= $b_last_num['read_num']) {
array_pop($b_top_rank);
} else {
array_pop($a_top_rank);
}
$len = count($a_top_rank) + count($b_top_rank);
}
$result = array_merge($a_top_rank,$b_top_rank);
//二维数组以字段read_num排序
array_multisort(array_column($result,'read_num'),SORT_DESC,$result);
dump($result);
}
执行代码得到的结果如下:
分别查看各自表阅读数前10的数据:
方法二
后面想了一下,直接用一句sql也能得出结果:
SELECT * FROM
(
SELECT * FROM read_log_a
UNION ALL
SELECT * FROM read_log_b
)
AS combine_table
ORDER BY read_num DESC LIMIT 10