在园中大V深蓝医生中的一篇文中发现了这个问题,感觉挺有意思。

问题简化为“求解连续日期登录次数最大的用户”。至少连续2天都登录才能认为是连续日登录。

数据岛问题

这个问题让我联想到了数据岛问题,数据岛问题就是间隔相同且连续的一个数值区间。以下面的整型数据集合为例:

1,

4,

5,

7,

8,

11,

12,

13

以上示例中,间隔为1可以划分为[1,1],[4,5],[7, 8], [11,13]共4个数据岛。针对如何获取数据岛的解决方案我以后在详细来说明数据差距和数据岛这个问题。我们假设一个数据岛具有唯一一个标识符,只要找到每一个数据岛中的这个标识符那就可以通过分组聚合解决该问题。

针对以上示例中的获取数据岛的解决方案的T-SQL脚本如下:

-- 创建表变量

DECLARE @tblTestData TABLE (

Val INT NOT NULL

);

-- 向其插入数据

INSERT INTO @tblTestData (Val) VALUES

(1),

(4),

(5),

(7),

(8),

(11),

(12),

(13);

-- 分组聚合

SELECT MIN(T.Val) AS StartVal, MAX(T.Val) AS EndVal

FROM (

-- 获取每个数据岛的标识符

SELECT Val, val - ROW_NUMBER() OVER (ORDER BY Val ASC) AS grp

FROM @tblTestData

) AS T

GROUP BY T.grp;

GO

执行后的结果如下:

该问题解决方案

我们继续回到本文的问题啦,简介了数据岛问题,我们显然针对该问题进行分拆如下:

第一步:先找到每个用户的登录日所在数据岛的唯一标识符;

第二步:通过用户和数据岛唯一分隔符分组聚合获得每个用户的连续陆登录日计数和登录次数之和;

第三步:针对第二步的结果以连续陆登录日计数倒序和登录次数之和倒序来获取结果。

准备测试数据

相关的T-SQL脚本如下:

IF OBJECT_ID(N'dbo.UserLoginInfo', N'U') IS NOT NULL

BEGIN

DROP TABLE dbo.UserLoginInfo;

END

GO

-- create testing table UserLoginInfo

CREATE TABLE dbo.UserLoginInfo (

ID INT IDENTITY(1, 1) PRIMARY KEY,

Name VARCHAR(50) NOT NULL,

LoginTime DATETIME NOT NULL

);

GO

-- insert testing data

INSERT dbo.UserLoginInfo (Name, LoginTime) VALUES

('zhang', '2015-11-10 12:01:50')

,('li', '2015-11-11 11:01:50')

,('wang', '2015-11-9 11:01:50')

,('zhang', '2015-11-11 12:01:50')

,('li', '2015-11-11 12:01:50')

,('wang', '2015-11-11 11:01:50')

,('zhang', '2015-11-12 12:01:50')

,('li', '2015-11-13 13:01:50')

,('wang', '2015-11-12 11:01:50')

,('zhang', '2015-11-13 12:01:50')

,('li', '2015-11-14 11:01:50')

,('wang', '2015-11-14 11:01:50')

,('zhang', '2015-11-10 12:01:50')

,('li', '2013-10-05 11:01:50')

,('li', '2013-10-06 11:01:50')

,('li', '2014-10-05 11:01:50')

,('li', '2014-10-06 11:01:50')

,('li', '2015-10-05 11:01:50')

,('li', '2015-10-06 11:01:50')

,('li', '2015-11-10 11:01:50')

,('li', '2015-11-11 11:01:50')

,('wang', '2015-11-09 11:01:50')

,('zhang', '2015-11-11 12:01:50')

,('li', '2015-11-11 12:01:50')

,('wang', '2015-11-11 11:01:50')

,('zhang', '2015-11-12 12:01:50')

,('li', '2015-11-13 13:01:50')

,('wang', '2015-11-12 11:01:50')

,('zhang', '2015-11-13 12:01:50')

,('li', '2015-11-14 11:01:50')

,('wang', '2015-11-14 11:01:50');

GO

通过执行以下T-SQL语句:

SELECT ID, Name, LoginTime

FROM dbo.UserLoginInfo;

GO

得到的结果如下:

注意:以上T-SQL来自园中深蓝医生的,我在此基础上进行了调整。

在第一步开始之前,通过查看用户登录信息表中的记录,可以发现:同一个用户一天可以登录多次。为了数据方便汇总,则进行分组汇总每个用户每天的登录次数。

合并每个用户每天的登录次数的T-SQL代码如下:

-- 0、分组汇总每个用户同一天登录的次数

SELECT T.Name, T.LoginDate, COUNT(0) AS DayLoginTimes

FROM (

SELECT ID, Name, LoginTime, CAST(CONVERT(VARCHAR(10), LoginTime, 120) AS DATE) AS LoginDate

FROM dbo.UserLoginInfo

) AS T

GROUP BY T.Name, T.LoginDate;

GO

执行后的结果如下:

我们来实现第一步获取每个用户的登录日所在的数据岛的唯一标识符,实现的T-SQL代码如下:

-- 1、获取每个用户的每个登录日所在的数据岛的唯一标识符

SELECT T.Name, T.LoginDate, T.DayLoginTimes, DATEADD(DAY, -1 * DENSE_RANK() OVER (PARTITION BY T.Name ORDER BY T.LoginDate ASC), T.LoginDate) AS GRP

FROM (

-- 0、分组汇总每个用户同一天登录的次数

SELECT T.Name, T.LoginDate, COUNT(0) AS DayLoginTimes

FROM (

SELECT ID, Name, LoginTime, CAST(CONVERT(VARCHAR(10), LoginTime, 120) AS DATE) AS LoginDate

FROM dbo.UserLoginInfo

) AS T

GROUP BY T.Name, T.LoginDate

) AS T

GO

执行后的结果如下:

第二步那就分组汇总每个用户的连续登录日的计数和登录次数之和,实现的T-SQL代码如下:

-- 2、分组汇总每个用户的连续登录日的计数和登录次数之和

SELECT T.Name, T.GRP, COUNT(T.LoginDate) AS LoginDateCount, SUM(T.DayLoginTimes) AS LoginTimesTotal

FROM (

-- 1、获取每个用户的每个登录日所在的数据岛的唯一标识符
SELECT T.Name, T.LoginDate, T.DayLoginTimes, DATEADD(DAY, -1 * DENSE_RANK() OVER (PARTITION BY T.Name ORDER BY T.LoginDate ASC), T.LoginDate) AS GRP
FROM (
-- 0、分组汇总每个用户同一天登录的次数
SELECT T.Name, T.LoginDate, COUNT(0) AS DayLoginTimes
FROM (
SELECT ID, Name, LoginTime, CAST(CONVERT(VARCHAR(10), LoginTime, 120) AS DATE) AS LoginDate
FROM dbo.UserLoginInfo
) AS T
GROUP BY T.Name, T.LoginDate
) AS T

) AS T

GROUP BY T.Name, T.GRP

GO

执行后的结果如下:

第三步那就很简单,直接通过用户的连续登录日计数和登录次数均倒序排序,实现的T-SQL代码如下:

-- 3、通过用户的连续登录日计数和登录次数均倒序排序得到的查询结果

SELECT T.Name, T.LoginDateCount, T.LoginTimesTotal

FROM (

-- 2、分组汇总每个用户的连续登录日的计数和登录次数之和

SELECT T.Name, T.GRP, COUNT(T.LoginDate) AS LoginDateCount, SUM(T.DayLoginTimes) AS LoginTimesTotal

FROM (

    -- 1、获取每个用户的每个登录日所在的数据岛的唯一标识符
SELECT T.Name, T.LoginDate, T.DayLoginTimes, DATEADD(DAY, -1 * DENSE_RANK() OVER (PARTITION BY T.Name ORDER BY T.LoginDate ASC), T.LoginDate) AS GRP
FROM (
-- 0、分组汇总每个用户同一天登录的次数
SELECT T.Name, T.LoginDate, COUNT(0) AS DayLoginTimes
FROM (
SELECT ID, Name, LoginTime, CAST(CONVERT(VARCHAR(10), LoginTime, 120) AS DATE) AS LoginDate
FROM dbo.UserLoginInfo
) AS T
GROUP BY T.Name, T.LoginDate
) AS T
) AS T
GROUP BY T.Name, T.GRP

) AS T

ORDER BY T.LoginDateCount DESC, T.LoginTimesTotal DESC;

GO

执行后的结果如下:

上图中红色矩形框标识的就是我们寻找的答案。

注意:因为我们开始第一步前的处理保证了每个用户一个登录日只有一次登录数据,第一步获取GRP列时,使用了DENSE_RANK密度排名窗口函数,其OVER字句中的排序字句具有唯一确定性,当然也可以使用ROW_NUMBER行号窗口函数和RANK排名窗口函数。

园中博友如有其他更好的解决方案,也请不吝赐教,万分感谢。

SQL Server求解连续操作(登录)数量(次数)最大的记录(用户)问题的更多相关文章

  1. SQL SERVER 2008 服务器登录名、角色、数据库用户、角色、架构的关系

    sql server登录名.服务器角色.数据库用户.数据库角色.架构区别联系 1.一个数据库用户可以对应多个架构(架构是表容器).架构里面包含的是数据库表. 2.一个数据库角色有可能涉及多个架构.数据 ...

  2. SQL server 如何修改登录名和密码

    No :1 启动SQL Server Management Studio,用windows登录进入: No :2 在左侧对象资源处理器中找到根节点,也就是你安装sqlserver时注册的服务器名称.然 ...

  3. SQL Server 本地数据库登录不上 解决方法

    sql本地数据库登录不了的话.先看看自己计算机 服务 SQL server  (MSSQLSERVER)  没有打开的话,请打开. 今天说的情景模式是  你误删了windows登录:禁用了sa登录:s ...

  4. 看看如何解决“SQL Server只能使用Windows身份登录,不能使用sa等Sql server身份进行登录”的问题

    今天安装Sql Server之后,出现SQL Server只能使用Windows身份登录,不能使用sa等Sql server身份进行登录的问题是由于sql server只设置了Windows身份验证, ...

  5. SQL Server 2008 错误15023:当前数据库中已存在用户或角色

    解决SQL Server 2008 错误15023:当前数据库中已存在用户或角色,SQLServer2008,错误15023,在使用SQL Server 2008时,我们经常会遇到一个情况:需要把一台 ...

  6. 转:Sql Server中清空所有数据表中的记录

    如果要删除数据表中所有数据只要遍历一下数据库再删除就可以了,清除所有数据我们可以使用搜索出所有表名,构造为一条SQL语句进行清除了,这里我一一给各位同学介绍.   使用sql删除数据库中所有表是不难的 ...

  7. Sql Server中清空所有数据表中的记录

    Sql Server中清空所有数据表中的记录 清空所有数据表中的记录: 代码如下:exec sp_msforeachtable  @Command1 ='truncate table ?'删除所有数据 ...

  8. 如何在Sql Server中读取最近一段时间的记录,比如取最近3天的或最近3个月的记录。

    如何在Sql Server中读取最近一段时间的记录,比如取最近3天的或最近3个月的记录. 主要用到DATEADD函数,下面是详细语句 取最近3天 select * from 表名where rq> ...

  9. 慎重管理SQL Server服务的登录(启动)账户和密码

    今天是大年初三,先跟大家拜个年,祝大家新年快乐.今天处理了一个alwaysOn问题——辅助副本因为磁盘空间不足一直显示[未同步——可疑],在日志中可以看到数据库处于挂起状态,与主副本失去同步.原以为只 ...

随机推荐

  1. [javaSE] 反射-Class类的使用

    JAVA-Reflect 专题 ①Class类的使用 ②方法的反射 ③成员变量的反射 ④构造函数的反射 ⑤java类的加载机制 Ⅰ在面向对象的世界里,万事万物皆为对象 类是对象,类是java.lang ...

  2. 实体类在Windows程序中的高级应用--------------------》》心境是一种境界。

    一.事务 我们在大家学到这,或多或少对事务都有一些理解了.今天的我也对事务有了更深一层的理解对我来说,我想与大家一起分享一下. 解析: 1.ADO.NET提供了事务处理功能 2.C#中开启事务 3.在 ...

  3. 为友盟消息推送开发的PHP SDK(composer版):可以按省发Android push

    一直以来APP希望按省市县推送Android push,只能自己分析用户经纬度,打tag发送. 现在终于有服务商提供了. 友盟消息推送 可以“按省推送”,很方便. 我为友盟做了PHP SDK(comp ...

  4. Exchange 2013 、Lync 2013、SharePoint 2013 三

    前两篇介绍的是关于Exchange 与 Lync 之间的配制关系,这一篇介绍关于Lync.Exchange 与 SharePoint 之间建立信任关系. 首先要创建基于SSL的SharePoint A ...

  5. 20款响应式的 HTML5 网页模板【免费下载】

    下面的列表集合了20款响应式的 HTML5 网页模板,这些专业的模板能够让你的网站吸引很多的访客.除了好看的外观,HTML5 模板吸引大家的另一个原因是由于其响应性和流动性.赶紧来看看. 您可能感兴趣 ...

  6. 一步一步教你如何解锁被盗的iPhone 6S

    即使你的iPhone6S设置了六位数的密码,甚至还设置了touch ID,但我要告诉你的是:你的手机仍然能被犯罪分子解锁. 事件背景 三天前,一位苹果用户的iPhone6S被偷了.随后,小偷重置了该用 ...

  7. 【Leafletjs】6.Control.Loading推展-在地图上边框添加加载动态条

    在已有的Control.Loading控件基础上结合CSS3 animation属性实现 .nz-loading .nz-loader { display: block; -webkit-animat ...

  8. android加固系列—2.加固前先要学会破解,调试内存值修改程序走向

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5138585.html] 因公司项目需要对app加固,经过本人数月的研究,实现了一套完整的仿第三 ...

  9. 【C语言】C语言数据类型

    目录: [数据类型图] [基本数据类型]   · 整型   · 实型   · 字符型   · 布尔类型 1.数据类型图 2.基本数据类型 · 整型 用于准确表示整数,根据表示范围的不同分为三种:短整型 ...

  10. Android 从Gallery获取图片

    本文主要介绍Android中从Gallery获取图片 设计项目布局 <LinearLayout xmlns:android="http://schemas.android.com/ap ...