一个有趣的 SQL 查询

一个朋友有这样一个SQL查询需求: 
有一个登录表(tmp_test),包含用户ID(uid)和登录时间(login_time)。表结构如下:

*************************** 1. row ***************************
Field: uid
Type: int(10) unsigned
Null: NO
Key: MUL
Default: NULL
Extra:
*************************** 2. row ***************************
Field: login_time
Type: timestamp
Null: NO
Key: MUL
Default: 0000-00-00 00:00:00
Extra:
问如何查询出所有在某一段时间内(如:2012-1-1至2012-1-17)连续7天都有登录的用户。

在写这个SQL时,发现一些很有意思东西,也许对大家写SQL有帮助,因此记录一下。

- 基本思路 Loop Join
  首先想到的思路是一个类似于Loop Join的方法:
  A. 取出2012-1-1到2012-1-11的每一条记录.
  B. 对取出的每一条记录,再去表中查询这个用户的接下来6天的记录。
     如果总数为6条记录,则满足连续7天的条件

- Range Join
  Loop Join的思路可以通过一个Join语句来实现。姑且称之为Range Join。通常join时,使用的都是
  等值join. 如果join列的值是唯一的,那么就是左表的一条记录对应右表的一条记录。而Range Join
  中,左表的一行数据对应右表的一个范围内的所有记录。

SQL 语句为:

SELECT DISTINCT t.uid FROM tmp_test AS t JOIN tmp_test AS t1
ON date(t.login_time) + 1 <= date(t1.login_time) AND
date(t.login_time) + 7 > date(t1.login_time) AND
t.uid = t1.uid
WHERE t.login_time BETWEEN ’2012-1-1 00:00:00′ AND ’2012-1-11 23:59:59′ AND
t1.login_time >= ’2012-1-2′ AND t.login_time < ’2012-1-18′(可去掉)

- COUNT(DISTINCT)
  “计算连续7天”,可以通过GROUP BY分组和COUNT()来完成。因为一个用户在1天内可能会有多次登录,
  这里需要使用(COUNT DISTINCT). SQL 语句为:

GROUP BY t.login_time, t.uid
HAVING COUNT(DISTINCT date(t1.login_time))=6

- BIT_OR
  考虑到DISTINCT操作需要缓存数据,就想到了用bit逻辑运算(可能会效率高一些)。因为连续的七天
  与第一天的差分别为,1,2,3,4,5,6,7.可以分别用1-7bit位来表示。根据这个特点,可以对分组中
  的每一行进行或(|)运算.如果最后的值等于b’1111110′(6个1).那么就是连续的7天。这个办法可以
  避免DISTINC操作。没想到MySQL中真的有了bit操作的聚合函数。BIT_OR就是我们要用的。

SQL 语句为:

GROUP BY t.login_time, t.uid
HAVING BIT_OR(1 << datediff(t1.login_time, t.login_time)) = b’1111110′;

- 去掉Range Join
  虽说上面的思路实现了这个查询要求,但是由于使用了Range Join,效率并不好。在对uid建索引的情
  况下,大约需要3.5s(总共约50000条记录). 有没有更好的方法呢?
  受BIT_OR的启发,可以通过单表扫描,用bit位来记录每个用户2012-1-1至2012-1-17是否有登录。
  然后根据这个值来判断是否有连续7天的情况。

我们需要一个辅助的函数来进行bit的运算:

DELIMITER |
/* 判断一个Bit序列中,是否存在若干个连续的1 */
/* 参数bits: bit序列*/
/* 参数trait: 指定的若干连续的1.如b’111111‘ */
CREATE FUNCTION bits_find_N1(bits BIGINT, trait BIGINT)
RETURNS BOOL
BEGIN
WHILE bits <> 0 DO
IF ((bits & trait) = trait) THEN
RETURN TRUE;
END IF;
SET bits = bits >> 1;
END WHILE;
RETURN FALSE;
END|
DELIMITER ;

SQL 语句为:

SELECT uid AS bit FROM tmp_test
WHERE login_time BETWEEN ’2012-1-1 00:00:00′ AND ’2012-1-17 23:59:59′
GROUP BY uid
HAVING bits_find_N1(BIT_OR(1 << datediff(login_time, ’2012-1-1′)),
b’1111111′) IS TRUE;

这个语句效率还是比较好的,即使不对uid建索引,也只需约0.27s

- 超高效率的语句
  下面是另一个朋友写的SQL,虽然有点复杂,但是效率超高,只需要约0.17s是这样的

SET @wy=0;
SELECT DISTINCT uid
FROM (SELECT MAX(date)-MIN(date) less,uid
FROM (SELECT date-rn diff, uid, date, rn
FROM (SELECT @wy:=@wy+1 rn, uid,
datediff(login_time,’1971-01-01′) date,login_time
FROM (SELECT date(login_time) login_time, uid FROM tmp_test
WHERE login_time>=’2012-01-01 00:00:00′ AND
login_time <’2012-01-18 00:00:00′
GROUP BY uid, date(login_time)
ORDER BY uid, date(login_time)
)x
)x
)x
GROUP BY diff,uid
)x
WHERE less>=6;

http://www.oschina.net/question/28_41179?sort=default&p=1

SELECT DISTINCT t.USER_ID FROM T_SD_COMMENT AS t JOIN T_SD_COMMENT AS t1
ON date(t.CREATE_TIME) + 1 <= date(t1.CREATE_TIME) AND
date(t.CREATE_TIME) + 7 > date(t1.CREATE_TIME) AND
t.USER_ID = t1.USER_ID
GROUP BY t.CREATE_TIME, t.USER_ID
HAVING( BIT_OR(1 << datediff(t1.CREATE_TIME, t.CREATE_TIME)) = b'' );
SELECT DISTINCT USER_ID
FROM (SELECT MAX(date) - MIN(date) AS less, USER_ID
FROM (SELECT date - rn AS diff, USER_ID , date, rn
FROM (SELECT @wy := @wy + 1 AS rn, USER_ID, datediff(CREATE_TIME, '1971-01-01') AS date, CREATE_TIME
FROM (SELECT date(CREATE_TIME) AS CREATE_TIME, USER_ID
FROM T_SD_COMMENT, (SELECT @wy := 0
) w
GROUP BY USER_ID, date(CREATE_TIME)
ORDER BY USER_ID, date(CREATE_TIME)
) x
) x
) x
GROUP BY diff, USER_ID
) x
WHERE less >= 6

一个有趣的 SQL 查询(查询7天连续登陆)的更多相关文章

  1. 一个有趣的SQL Server 层级汇总数据问题

        看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题.          具体的问题如下:     parent_id emp_id emp_nam ...

  2. 一个有趣的 SQL 查询

    来源:站长资讯 一个朋友有这样一个SQL查询需求: 有一个登录表(tmp_test),包含用户ID(uid)和登录时间(login_time).表结构如下: ********************* ...

  3. 分享一个绿色版本 sql server 查询器,

    首先感谢原作者 源码来原于网络 此查询器增加了一些功能, 可以高亮显示 导出sql 语句    可以把左边树型拖入脚本编辑器 http://pan.baidu.com/s/1dDjaSbn

  4. [转]一个用户SQL慢查询分析,原因及优化

    来源:http://blog.rds.aliyun.com/2014/05/23/%E4%B8%80%E4%B8%AA%E7%94%A8%E6%88%B7sql%E6%85%A2%E6%9F%A5%E ...

  5. 怎样用SQL语句查询一个数据库中的所有表?

    怎样用SQL语句查询一个数据库中的所有表?  --读取库中的所有表名 select name from sysobjects where xtype='u'--读取指定表的所有列名select nam ...

  6. 怎么用sql语句查询一个数据库有多少张表

    今天在技术群中闲谈时忽然聊到一个问题,那就是当一个数据库中有多张表时怎么快速的获取到表的个数,从而给问询者一个准确的回答. 大家或许会说,这个问题和我们的数据库操作没有太大关系或者不是很挂钩,所以没意 ...

  7. SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比

    原文:SQL点滴10-使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比 今天偶尔看到sql中也有with关键字,好歹也写了几年的sql语句,居然第一次接触,无知啊.看了一位博主的文章 ...

  8. MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架

    MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用 ...

  9. Linq to SQL 语法查询(链接查询,子查询 & in操作 & join,分组统计等)

    Linq to SQL 语法查询(链接查询,子查询 & in操作 & join,分组统计等) 子查询 描述:查询订单数超过5的顾客信息 查询句法: var 子查询 = from c i ...

随机推荐

  1. 英特尔实感SDK 代码示例

    原文地址 摘要 本套代码示例针对巴西英特尔实感动手实验室创建,旨在帮助参与人员了解如何使用英特尔® 实感™ 软件开发套件. 12 个示例使用 C# SDK 包装程序,提供了简单的基于控制台的应用,支持 ...

  2. 验证证书的安装之外部用户PC

      背景:使用一个域外的用户进行登录并验证   1.         用户登录浏览器下载CA证书或者证书链   2.         下载   3.         安装证书   4.         ...

  3. sort()的多种用法

    sort()  方法用于对数组的元素进行排序. 一.默认情况 在默认情况下, sort() 方法按升序排列数组项.为了实现排序, sort() 方法会调用每个数组项的 toString() 转型方法, ...

  4. Fragstats软件使用及其景观生态学意义

    [转]Fragstats软件使用及其景观生态学意义     原文地址:http://blog.163.com/shuailai@126/blog/static/13238040820104152513 ...

  5. CodeForces 628D Magic Numbers (数位dp)

    题意:找到[a, b]符合下列要求的数的个数. 1.该数字能被m整除 2.该数字奇数位全不为d,偶数位全为d 分析: 1.dp[当前的位数][截止到当前位所形成的数对m取余的结果][当前数位上的数字是 ...

  6. 【风马一族_Java】如何获取ACSLL表的值

    消耗两小时,只为一代码. 终于得到了此代码: public class sows { public static void main(String[] args) { byte[] bytes = n ...

  7. Chipscope 仿真VmodCAM IIC程序

    Chipscope 仿真VmodCAM IIC程序: 目的:熟悉EDK中建立chipscope 注意:zedboard使用digilent USB下载时,chipscope不能和SDK同时使用,否则芯 ...

  8. Mysql 存储程序

    #1存储过程create procedure greeting() BEGIN # 77 = 16 FOR username + 60 for hostname + 1 for '@' DECLARE ...

  9. Java中提供的工具类

    System.arraycopy介绍 (1).System.arraycopy用于拷贝数组 arraycopy(Object src, int srcPos, Object dest, int des ...

  10. js设计模式(8)---享元模式

    0.前言 今天总结了四种设计模式,到现在有点精疲力尽了,但是还是有不少收获,很开心自己有掌握了新的东西,今天变得有了价值. 1.使用条件 1.1.网页中使用了大量资源密集型的对象: 1.2.这些对象中 ...