Mysql如何查询连续的时间次数
2018-03-04 00:55
381 查看
在网上看到一道有意思的题目,大意是如何在mysql查询连续的时间内登录的次数。原文链接:http://www.oschina.net/question/573517_118821原文中相关说明很少,看起来很费劲,故在这里,我自己添加了些说明,完全是自己理解的,若有不对,欢迎批评指正,谢谢。在网上看到一道有意思的题目,大意是如何在mysql查询连续在线的天数。所谓的连续在线是指相邻两天都登录过,不一定一直在线,但是只要有过登录即可。如,1月1日登录了一会下线了,一月2日登录了一会下线了,那么,就算1月1日和2日是连续在线,天数为2。如果1月3日,没有登录过,1月4日登录了,此
4000
时,1月4日与1月1日、2日不算连续在线了。设连续在线天数为days,则1月1日在线,days=1;1月2日在线,days=2;1月3日不在线,1月4日在线,days=1;1月5日在线,days=2; 首先建表,填充测试数据:CREATE TABLE `tmysql_test_lianxu_3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`sts` datetime DEFAULT NULL COMMENT '登录时间',
`ets` datetime DEFAULT NULL COMMENT '离线时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin 测试数据为:INSERT INTO `tmysql_test_lianxu_3` VALUES (1, 1, '2014-1-1 21:00:00', '2014-1-2 07:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (2, 1, '2014-1-2 15:37:57', '2014-1-2 21:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (3, 2, '2014-1-1 09:00:00', '2014-1-1 15:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (4, 2, '2014-1-2 09:00:00', '2014-2-1 16:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (5, 1, '2014-1-4 10:00:00', '2014-1-4 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (6, 1, '2014-1-5 12:00:00', '2014-1-5 13:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (7, 2, '2014-1-10 00:00:00', '2014-1-10 06:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (8, 2, '2014-1-11 13:00:00', '2014-1-11 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (10, 2, '2014-1-12 12:00:00', '2014-1-12 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (11, 1, '2014-1-8 06:00:00', '2014-1-8 16:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (12, 2, '2014-1-11 21:00:00', '2014-1-12 06:00:00'); 在Oracle中可以使用row_number搞定,mysql中怎么做呢? 可以参考链接: http://www.explodybits.com/2011/11/mysql-row-number/ 首先看原文中给出的答案:SELECT uid, days, COUNT(*) AS num
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt) = 1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, days; 也是使用了mysql模拟oracle的row_number函数。 运行结果是:
我看了半天发现结果好像不是我想要的,我想要的是要有开始时间,结束时间之类的。 看下中间表再说:说明@cont_day用来统计连续在线的天数,当不是同一个用户或者是同一用户但在线不连续@cont_day都为1。当时同一用户(@last_uid=uid)并且天数连续(DATEIFF(login_dt, @last_dt) <=1),@last_uid用来记录上一条查询记录的uid,@last_dt用来记录上一条查询记录的登录时间。关键在于@cont_ix,@cont_ix表示连续在线的区段数目,当然也就对应区段号。如,1月1日在线,cont_ix=1;1月2日在线,cont_ix=1,因为这两天的登录时连续的,如果1月3日不在线,1月4日又登录了,此时@cont_ix=2;1月5日登录了,@cont_ix=2;没有变,以为1月4、5日的在线是连续的天,故算作同一个区段。而在什么情况下才会产生新的区段即@cont_ix =@ cont_ix+1呢?情况(1)新的用户出现;(2)是原来用户但是不是连续天 。故当@last_uid = uidAND DATEDIFF(login_dt, @last_dt)=1不成立,则@cont_ix =@ cont_ix+1。SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1 结果为:
看了下可以这么做,对于同一个用户的某个区段而言,取区段中最大连续日期days为连续日期,最小login_day为开始时间,最大login_day为结束时间,故可写成如下格式:SELECT uid, max(days) lianxu_days, min(login_day) start_date,max(login_day) end_date
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, cont_ix;
这里存在的问题是:表里面的的sts登录时间不能有2条uid相同时间在同一天内。 解决方法是:在case中添加一个<1 的判断条件 SELECT uid, max(days) lianxu_days, min(login_day) start_date,max(login_day) end_date
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)<1) THEN
(@cont_day + 0)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, cont_ix; 存在的问题: 时间sts的时分秒不见了。 欢迎各位留下更好的查询SQL,如本文中的SQL有问题也请指出,谢谢。
4000
时,1月4日与1月1日、2日不算连续在线了。设连续在线天数为days,则1月1日在线,days=1;1月2日在线,days=2;1月3日不在线,1月4日在线,days=1;1月5日在线,days=2; 首先建表,填充测试数据:CREATE TABLE `tmysql_test_lianxu_3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`sts` datetime DEFAULT NULL COMMENT '登录时间',
`ets` datetime DEFAULT NULL COMMENT '离线时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin 测试数据为:INSERT INTO `tmysql_test_lianxu_3` VALUES (1, 1, '2014-1-1 21:00:00', '2014-1-2 07:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (2, 1, '2014-1-2 15:37:57', '2014-1-2 21:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (3, 2, '2014-1-1 09:00:00', '2014-1-1 15:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (4, 2, '2014-1-2 09:00:00', '2014-2-1 16:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (5, 1, '2014-1-4 10:00:00', '2014-1-4 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (6, 1, '2014-1-5 12:00:00', '2014-1-5 13:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (7, 2, '2014-1-10 00:00:00', '2014-1-10 06:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (8, 2, '2014-1-11 13:00:00', '2014-1-11 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (10, 2, '2014-1-12 12:00:00', '2014-1-12 18:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (11, 1, '2014-1-8 06:00:00', '2014-1-8 16:00:00');
INSERT INTO `tmysql_test_lianxu_3` VALUES (12, 2, '2014-1-11 21:00:00', '2014-1-12 06:00:00'); 在Oracle中可以使用row_number搞定,mysql中怎么做呢? 可以参考链接: http://www.explodybits.com/2011/11/mysql-row-number/ 首先看原文中给出的答案:SELECT uid, days, COUNT(*) AS num
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt) = 1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, days; 也是使用了mysql模拟oracle的row_number函数。 运行结果是:
我看了半天发现结果好像不是我想要的,我想要的是要有开始时间,结束时间之类的。 看下中间表再说:说明@cont_day用来统计连续在线的天数,当不是同一个用户或者是同一用户但在线不连续@cont_day都为1。当时同一用户(@last_uid=uid)并且天数连续(DATEIFF(login_dt, @last_dt) <=1),@last_uid用来记录上一条查询记录的uid,@last_dt用来记录上一条查询记录的登录时间。关键在于@cont_ix,@cont_ix表示连续在线的区段数目,当然也就对应区段号。如,1月1日在线,cont_ix=1;1月2日在线,cont_ix=1,因为这两天的登录时连续的,如果1月3日不在线,1月4日又登录了,此时@cont_ix=2;1月5日登录了,@cont_ix=2;没有变,以为1月4、5日的在线是连续的天,故算作同一个区段。而在什么情况下才会产生新的区段即@cont_ix =@ cont_ix+1呢?情况(1)新的用户出现;(2)是原来用户但是不是连续天 。故当@last_uid = uidAND DATEDIFF(login_dt, @last_dt)=1不成立,则@cont_ix =@ cont_ix+1。SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1 结果为:
看了下可以这么做,对于同一个用户的某个区段而言,取区段中最大连续日期days为连续日期,最小login_day为开始时间,最大login_day为结束时间,故可写成如下格式:SELECT uid, max(days) lianxu_days, min(login_day) start_date,max(login_day) end_date
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, cont_ix;
-- 注意最后的分组条件:按用户名和区段号同时分组,然后再对每个区段进行处理。结果是:
这里存在的问题是:表里面的的sts登录时间不能有2条uid相同时间在同一天内。 解决方法是:在case中添加一个<1 的判断条件 SELECT uid, max(days) lianxu_days, min(login_day) start_date,max(login_day) end_date
FROM (SELECT uid,
@cont_day :=
(CASE
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)=1) THEN
(@cont_day + 1)
WHEN (@last_uid = uid AND DATEDIFF(login_dt, @last_dt)<1) THEN
(@cont_day + 0)
ELSE
1
END) AS days,
(@cont_ix := (@cont_ix + IF(@cont_day = 1, 1, 0))) AS cont_ix,
@last_uid := uid,
@last_dt := login_dt login_day
FROM (SELECT uid, DATE(sts) AS login_dt
FROM tmysql_test_lianxu_3
ORDER BY uid, sts) AS t,
(SELECT @last_uid := '',
@last_dt := '',
@cont_ix := 0,
@cont_day := 0) AS t1) AS t2
GROUP BY uid, cont_ix; 存在的问题: 时间sts的时分秒不见了。 欢迎各位留下更好的查询SQL,如本文中的SQL有问题也请指出,谢谢。
相关文章推荐
- Mysql如何查询连续的时间次数
- Mysql如何查询连续时间内的次数
- mysql如何查询两个日期之间最大的连续登录天数
- Mysql查询数据IO次数如何计算
- 如何使用MYSQL时间戳查询时间段
- Mysql中如何对按年月日查询时间字段
- mysql如何查询两个日期之间最大的连续登录天数
- mysql查询连续时间数据——无数据补0
- MySQL5.6 如何优化慢查询的SQL语句 -- SQL优化
- mysql中DATE_FORMAT用法根据时间按周、月、年等查询
- MySql如何分级查询上千万行记录的表,并将该大数据显示到界面中
- mysql时间格式化,按时间段查询的MySQL语句
- 一步一步优化你的mysql------如何优化数据库中的查询语句(一)
- mysql中如何设置默认时间为当前时间
- Mysql 查询时间转换
- php中查询mysql 如何在WHERE meta_id IN 中用数组
- mysql 分组查询以及显示各个分组所有的温度和时间要用GROUP_CONCAT
- mysql:如何合并两个查询的结果集的数据
- php 接口如何使同一用户在规定时间内访问规定的次数
- MySQL查询指定时间的数据