一个朋友有这样一个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′;

NOTE: 从测试结果看,没有索引时BIT_OR要比DISTINCT好一点点,不是非常明显。当DISTINCT的
        字段上有索引时,要比BIT_OR要好一点点.

- 去掉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;

附上测试数据,供大家验证。tmp_test
由于用的是timestamp类型,导入后时间可能会有变化,导致结果不一样。我们测试的结果有183,185两种。
另外:用户可以在同一秒内登录多次,即出现多条相同的记录。
如uid=1, login_time=’2012-1-1 00:00:00′ 会出现多次。

转载于:https://blog.51cto.com/jilson/1055350

sql range 范围内查询相关推荐

  1. SQL里各种联合查询总结

    SQL里各种联合查询总结 create database mytest go  www.2cto.com   use mytest go create table t_dept ( d_id int ...

  2. SQL语言之子查询(Oracle)

    子查询(内查询) 一个select语句嵌套在另一个select语句中的子句: 例 可以用组合两个查询的方法解决这个问题,放置一个查询到另一个查询中.内查询或子查询返回一个值给外查询或主查询.使用一个子 ...

  3. SQL进阶,子查询与窗口函数

    本节给大家讲解SQL在实际过程中用途比较多的子查询与窗口函数,下面一起学习. 示例工具:MySQL8.0.Navicat Premium 12 本文讲解内容:子查询与窗口函数 适用范围:SQL进阶应用 ...

  4. sql 12天内的数据_想要在12周内成为数据科学家吗?

    sql 12天内的数据 重点 (Top highlight) I see many ads that claim to make you a data scientist in 12 weeks. T ...

  5. Linq to Sql: 集成数据库语言查询之一

    Linq to Sql: 集成数据库语言查询之一 2007-09-11 11:30:28 来源:天极yesky 作者:随风流月 带您探索"CRUD "操作-创建,接收,更新与删除, ...

  6. Mongo DB教程及SQL与Mongo DB查询的映射

    目录 介绍 在机器上设置Mongo DB 启动Mongo DB 下载RoboMongo MongoDB术语 MongoDB的要点 查询时间到了 MongoDB函数 MongoDB中的自动递增ID(SQ ...

  7. SQL语言_3 模糊查询和聚合函数

    SQL语言_3 模糊查询和聚合函数 作者:田超凡 版权所有,转载请注明原作者,仿冒侵权必究法律责任 1.数据库中查询的机制 查询是针对数据表中已存在的数据行而言的,可以将它简单理解为筛选,将符合条件的 ...

  8. SQL根据时间字段查询半小时之内或者之外的数据

    SQL根据时间字段查询半小时之内或者之外的数据 SELECT * FROM kunyao_shop_order WHERE create_time <= CURRENT_TIMESTAMP - ...

  9. SQL语句多表查询:【多表连查】和【子查询】

    SQL语句多表查询:[多表连查]和[子查询] 说明:insert.update.delete只针对[一张表]执行操作. 说明:select可以查询一张表.也可以查询多张表. 说明:多表查询分为:[多表 ...

最新文章

  1. python高级-模块(14)
  2. 12C -- DDL日志
  3. 用神经网络分类3*3矩阵
  4. JavaScript实现combine Without Repetitions不重复地结合算法(附完整源码)
  5. oracle TNS: 协议适配器错误 解决办法
  6. solr5.0mysql_ik扩展支持Solr配置详解
  7. Windows批处理符号简介、常用Dos命令
  8. python自动下载邮件附件_Python批量下载电子邮件附件并汇总合并Excel文件
  9. boost::asio 笔记
  10. 【Vegas原创】重建Exchange 2007 OWA的虚拟目录
  11. 软件工程第一次作业2018
  12. 牛客网剑指offer
  13. 【黑马程序员】新的开始
  14. Google发布Chrome Web Store应用商店
  15. Docker Macvlan
  16. 安踏的搜索引擎营销_案例:从安踏看IP跨界营销的内在逻辑
  17. 计算机重新启动进不去系统,电脑关机重启进不了系统怎么办
  18. 百度地图导航tts语音静音问题的解决
  19. 【小程序】小程序多次扫描不同二维码参数不生效问题
  20. 模拟支付宝、淘宝登录1

热门文章

  1. MySQL操作实战(一):关键字 函数
  2. Anaconda3-5.3.0-Windows-x86_64
  3. 重载session存储方式–session_set_save_handler()
  4. 逆向-攻防世界-reverse-box
  5. centos7系列Cobbler+kickstart全自动装机实战
  6. Typescript 和 Javascript之间的区别
  7. P2176 [USACO14FEB]路障Roadblock
  8. python基础——变量
  9. C语言调用WIN32 API学习之6鼠标与键盘响应
  10. PIL中分离通道发生“AttributeError: 'NoneType' object has no attribute 'bands'”