来源:站长资讯
一个朋友有这样一个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;

一个有趣的 SQL 查询的更多相关文章

  1. 一个有趣的 SQL 查询(查询7天连续登陆)

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

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

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

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

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

  4. 记一个简单的sql查询

    在我们做各类统计和各类报表的时候,会有各种各样的查询要求.条件 这篇主要记录一个常见的统计查询 要求如下: 统计一段时间内,每天注册人数,如果某天没有人注册则显示为0 现在建个简单的表来试试 建表语句 ...

  5. Android adb使用sqlite3对一个数据库进行sql查询

    sqlite是Android下集成的一个轻量级数据库,我们可以通过adb程序进入数据库命令行,对数据进行查询,具体操作如下: ①打开windows的cmd ②输入adb shell.此时进入了该安卓系 ...

  6. EJB3Persistence开发手册-原生SQL查询(NativeSQL)

    EJB3 QL对原生SQL做了非常好的支持.采用原生SQL做查询结果不但可以是象SQL中的返回列值,也可以是Entity类,甚至可以是两者的混合. EJB3 EntityManager接口定义了多个原 ...

  7. 黄聪:实用WordPress SQL查询方法

    为所有文章和页面添加自定义字段 这段代码可以为WordPress数据库内所有文章和页面添加一个自定义字段. 你需要做的就是把代码中的‘UniversalCutomField‘替换成你需要的文字,然后把 ...

  8. 在Delphi中动态地使用SQL查询语句 Adoquery sql 参数 冒号

    在Delphi中动态地使用SQL查询语句 在一般的数据库管理系统中,通常都需要应用SQL查询语句来提高程序的动态特性.下面介绍如何在Delphi中实现这种功能.在Delphi中,使用SQL查询语句的途 ...

  9. spring MVC +freemarker + easyui 实现sql查询和执行小工具总结

    项目中,有时候线下不能方便的连接项目中的数据源时刻,大部分的问题定位和处理都会存在难度,有时候,一个小工具就能实时的查询和执行当前对应的数据源的库.下面,就本人在项目中实际开发使用的小工具,实时的介绍 ...

随机推荐

  1. Python基础运算符(算数、比较、赋值、逻辑、成员)

    Python运算符有(算数运算符.比较运算符.赋值运算符.逻辑运算符.位运算符.成员运算符.身份运算符): 本程序包含算数.比较.赋值.逻辑.成员运算符. 1.运算符测试 #!/usr/bin/pyt ...

  2. python2.7 编码问题

    python 2.7编码问题,着实令人头疼不已,这两天抽闲想真正弄明白.需要弄清楚这个问题,首先需要明白ASCII,Unicode 和 UTF-8之间的关系. 进行对上述几种概念进行描述之前,先进行简 ...

  3. 一个农民工自学java找到工作的励志故事

    <!-----------------------------------------------------------------------------摘自网络-------------- ...

  4. 谷歌(Google)学术镜像,谷歌镜像

    谷歌(Google)学术镜像,谷歌镜像 2019-09-03 15:32:26 Hinton-wu 阅读数 6743 文章标签: 谷歌google学术镜像 更多 分类专栏: 其他   版权声明:本文为 ...

  5. 基于搜索的贝叶斯网络结构学习算法-K2

    基于搜索的贝叶斯网络结构学习算法-K2 2018-04-05 19:34:18 ItsBlue 阅读数 3172更多 分类专栏: 贝叶斯网络 网络结构学习   版权声明:本文为博主原创文章,遵循CC ...

  6. 转------深入理解--Java按值传递和按引用传递

    引言 最近刷牛客网上的题目时碰到不少有关Java按值传递和按引用传递的问题,这种题目就是坑呀,在做错了n次之后,查找了多方资料进行总结既可以让自己在总结中得到提高,又可以让其他人少走弯路.何乐而不为? ...

  7. Unity 自定义"=="操作符 [翻译来源blogs.unity3d,2014/05]

    主要内容来源 https://blogs.unity3d.com/cn/2014/05/16/custom-operator-should-we-keep-it/ 在我们代码里,如果有这样的代码: i ...

  8. ES6箭头函数及this指向

    箭头函数(=>):函数简写 无参数:() => {} 单个参数:x => {} 多个参数:(x, y) => {} 解构参数:({x, y}) => {} 嵌套使用:部署 ...

  9. vue的$nextTick使用后的js代码执行顺序问题

    一.问题产生背景: 父组件已经获得子组件实例,并能直接触发子组件的方法,在父组件中调用了子组件的两个方法 // 父组件调用子组件,this.picker是获取的子组件整个实例,先调用update,再调 ...

  10. linux c 错误的捕获

    经常在调用linux 系统api 的时候会出现一些错误,比方说使用open() write() creat()之类的函数有些时候会返回-1,也就是调用失败,这个时候往往需要知道失败的原因.这个时候使用 ...