场景
有一个礼物记录表,表名'send_gift_log',送一次礼物,增加一条记录。表里边有下边这些字段:
1. senderid (赠送人uid)
2. sendernickname (赠送人昵称)
3. giftid (礼物id)
4. giftname (礼物昵称)
5. giftcount (礼物个数)
6. accepterid (收礼人uid)
7. accepternickname (收礼人昵称)
8. logdate (赠送时间)
9. roomid (房间id)
10. ……
索引:senderid
,accepterid
,roomid
,giftid
。
赠送人的昵称随时会改变。如果赠送人昵称改变了,他再赠送礼物,表里就会记录他当前的昵称。同样,收礼人的昵称也会改变。
需求
- 要求,昵称用最新的。
-
在相同礼物数的情况下,以先达到优先排行。比如:
A,B,C三个送礼人,他们送出的礼物个数都是 1000个(可能分多次赠送),如果A最后一次赠送礼物的时间比B早,而B最后一次赠送礼物的时间比C早,那么A,B,C三人的排行顺序就是 A,B,C。
- 统计一段时间内,某个礼物或几个礼物,某些房间里的排行。
按以上要求,统计送礼人排行和收礼人排行。
分析
首先,不通过用户表去获取昵称,用户表非常庞大。
统计的是两类排行,可以做两次处理。以做送礼人排行为例。先SELECT sendernickname, SUM(giftcount) as gc
,再FROM, WHERE
,再GROUP BY senderid
,然后再ORDER BY gc
。
这时,一个排行已经出来了。可是,送礼人的最最新昵称没有得到。现在的想法是再嵌套一个SELECT
,将最新的昵称给取出来。完整的sql语句如下。
SELECT SUM(a.giftcount) as gc ,
(SELECT b.sendernickname FROM send_gift_log b WHERE
a.senderid = b.senderid ORDER BY b.logdate DESC limit 1) as nickname
FROM send_gift_log a
WHERE a.logdate > '2017-06-15 12:30:00' AND
a.logdate < '2017-06-15 23:59:59' AND a.roomid in('3344', '991',
'9696') AND a.giftid = 2017
GROUP BY a.senderid ORDER BY gc
当有几个人赠送礼物的个数相同时,这样写,排行也会出问题。并不能保证每次取的的数据顺序都是固定的,还有先赠送的排前边的逻辑。所以,还得进一步处理。既然上边取了赠送人最新昵称,那顺便把logdate
取出来不就可以了(他们属于同一行)。因为嵌套的SELECT
作为取出的一个字段,所以用CONCAT
将昵称和时间组在一起。在排序的时候,再用explode
拆出来就可以。完整sql语句如下。
SELECT SUM(a.giftcount) as gc ,
(SELECT CONCAT(b.sendernickname, '#--#', b.logdate) FROM
send_gift_log b WHERE a.senderid = b.senderid ORDER BY b.logdate
DESC limit 1) as nickname
FROM send_gift_log a
WHERE a.logdate > '2017-06-15 12:30:00' AND
a.logdate < '2017-06-15 23:59:59' AND a.roomid in('3344', '991',
'9696') AND a.giftid = 2017
GROUP BY a.senderid ORDER BY gc
功能虽然实现了,可是效率不好。在鱼和熊掌不能兼得间,怎么办呢。考虑是不是还有其他方法呢。
mysql里有个MAX
函数,既然时间是个数据标量,用MAX
不刚好吗。可是昵称呢,昵称还是没有办法。那就把最新昵称的需求丢掉吧。完整sql语句如下。
SELECT SUM(a.giftcount) as gc , MAX(a.logdate) as date
FROM send_gift_log a
WHERE a.logdate > '2017-06-15 12:30:00' AND a.logdate < '2017-06-15
23:59:59' AND a.roomid in('3344', '991', '9696') AND a.giftid = 2017
GROUP BY a.senderid ORDER BY gc
最后,将查询出来的数据进行排序
排序
$sort1 = array();
$sort2 = array();
$info = array();
while ($row = $result->fetch_assoc())
{
$sort1[] = $row['gc'];
$sort2[] = $row['date'];
$info[] = $row;
}
if(count($info) > 0)
{
array_multisort($sort1, SORT_DESC, $sort2, SORT_ASC, $info);
}
unset($sort1, $sort2);