2013-07-10 153 views
1

我有一个“会话”表中的其他领域有以下字段:选择最后N个唯一的记录mysql的

int int(11), 
user_id int(11), 
login_date timestamp, 
ip varchar(15) 

我想为每个用户选择的最后login_date最后三个独特的IPS。我怎样才能以优雅的方式做到这一点?

我找到了一个解决方案,但它非常非常非常丑陋,无效,耗时且涉及混合mysql和bash脚本。我怎样才能在MySQL中做到这一点?

P.S.该表有430万行cca。

一些样本数据:http://sqlfiddle.com/#!2/e9ddd

+7

你能告诉你“非常非常非常丑陋,无效,耗时”吗? – Raptor

+0

我从此表保存到文件唯一用户标识。然后,对于每个uid,我获得所有会话,按登录日期DESC对其进行排序并插入临时表中。第一行将包含最后一个会话,我从中选择group_concat(ip)(从temp_table limit 3中选择不同的ip) –

+0

这里的解决方案比你的稍好:http://sqlfiddle.com/#!2/9d65d/3。它仍然需要您的客户端代码遍历所有user_id。我确信有一种方法可以在单个查询中实现您要查找的内容。我只是不知道。 – xbonez

回答

3

花了很多时间从SQL SERVER

我有更好的表现在我的查询工作MYSQL转换。

select 
user_id, 
max(cast(login_date as datetime)), 
group_concat(distinct ip order by login_date desc SEPARATOR ' , ') 
from 
(
SELECT 
    user_id, 
    login_date, 
    ip, 
    CASE user_id 
    WHEN @curType 
    THEN @curRow := @curRow + 1 
    ELSE @curRow := 1 AND @curType := user_id 
    END as sequence 
FROM sessions, (SELECT @curRow := 0, @curType := '') r 
ORDER BY user_id asc,login_date desc 
)t 
where sequence<4 
group by user_id 

SQL FIDDLE

+0

@fancyPants请看看我更新的答案。 – Luv

+0

+1虽然不能确定,该IP属于最新的条目。但由于OP没有明确说明,所以此解决方案可能是正确的。 – fancyPants

0

这将让你的最后日期为每个user_id说明

SELECT s1.* FROM sessions s1 
LEFT OUTER JOIN sessions s2 
ON s1.user_id = s2.user_id AND s1.login_date < s2.login_date 
WHERE s2.user_id IS NULL 

,这将让你用于每个独立IP地址user_id说明

SELECT user_id, GROUP_CONCAT(DISTINCT(ip)) FROM sessions GROUP BY user_id

然后你可以混合它们, 让它们在一起会对资源有点沉重但我认为这是可行的。

祝你好运!

http://sqlfiddle.com/#!2/bb209/71

+1

上次登录日期可以更简单地选择 - 无需连接。 只为每个用户选择3个IP比将它们连接在一起困难得多。 –

+0

@Guy ** [Not working](http://sqlfiddle.com/#!2/e9ddd/61)** – Luv

0

你可以这样做:

SELECT * FROM (
    SELECT 
    user_id 
    ,ip, 
    ,login_date 
    ,IF(@user_id = SESSION_GRUOPED.user_id, @rownum:[email protected]+1, @rownum:=1) as row_id 
    ,(@user_id:= SESSION_GRUOPED.user_id) as group_id 
    FROM (
    SELECT user_id, ip, max(login_date) login_date FROM SESSION group by user_id, ip order by user_id, login_date desc 
) SESSION_GRUOPED 
    JOIN (SELECT @rownum := 0) r 
    JOIN (select @user_id := 0) u 
) RESULT 
WHERE row_id < 4; 

查询的关键是这部分

SELECT user_id, ip, max(login_date) login_date FROM SESSION group by user_id, ip order by user_id, login_date desc

多亏了它,你获取有关信息最后一次每个用户给定的IP地址。

然后,您可以将此设置为在组中设置结果编号,然后将结果限制为小于4的结果,因为对于单个用户只需要三个结果。

注意:如果将row_id < 4更改为row_id = 1,那么您将拥有最新的用户登录名。

+0

** [您的问题](http://sqlfiddle.com/#!2/e9ddd/57)** – Luv

+0

@Luv,非常感谢。我没有机会运行它。所以一个小错误已经找到了感谢你的链接。该订单对日期无效。现在很好;-)。 –