在园中大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. Java中使用Jedis操作Redis

    使用Java操作Redis需要jedis-2.1.0.jar,下载地址:http://files.cnblogs.com/liuling/jedis-2.1.0.jar.zip 如果需要使用Redis ...

  2. 跨平台日志清理工具 Log-Cutter v1.0.3 正式发布

    Log-Cutter 是JessMA开源组织开发的一个简单实用的日志切割清理工具.对于服务器的日常维护来说,日志清理是非常重要的事情,如果残留日志过多则严重浪费磁盘空间同时影响服务的性能.如果用手工方 ...

  3. GJM : Unity3D 常用网络框架与实战解析 【笔记】

    Unity常用网络框架与实战解析 1.Http协议          Http协议                  存在TCP 之上 有时候 TLS\SSL 之上 默认端口80 https 默认端口 ...

  4. [转]PHP5 session 详解

    来源:http://blog.163.com/lgh_2002/blog/static/4401752620105246517509/ http协议是WEB服务器与客户 端(浏览器)相互通信的协议,它 ...

  5. Linux IO函数的使用和区别

    Linux系统中的IO函数主要有read.write.recv.send.recvmsg.sendmsg.readv.writev,本篇主要介绍他们的使用以及区别. read函数: #include ...

  6. Date类型 方法

    Date 方法函数 Date.parse():可以获得该日期的毫秒数 var now=Date.parse('May 25,2005')//1085414400000 Date.UTC():也可以获得 ...

  7. JavaScript中的各种变量提升(Hoisting)

    首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确.因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升( ...

  8. abap 字符串处理

    1).SHIFT:截断字符串 SHIFT {c} [BY {n} PLACES] [{mode}].:      作用:去掉字符串的前n个位置的字符,如果n未指定,默认为1,如果指定的n小于等于0,则 ...

  9. linux下的服务器搭建集成环境

    linux下的服务器搭建集成环境 ——写给初学者的我们 1.准备工具 1.1 SecureCRT SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录 ...

  10. Java虚拟机JVM学习04 类的初始化

    Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...