SQL Server求解连续操作(登录)数量(次数)最大的记录(用户)问题
在园中大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求解连续操作(登录)数量(次数)最大的记录(用户)问题的更多相关文章
- SQL SERVER 2008 服务器登录名、角色、数据库用户、角色、架构的关系
sql server登录名.服务器角色.数据库用户.数据库角色.架构区别联系 1.一个数据库用户可以对应多个架构(架构是表容器).架构里面包含的是数据库表. 2.一个数据库角色有可能涉及多个架构.数据 ...
- SQL server 如何修改登录名和密码
No :1 启动SQL Server Management Studio,用windows登录进入: No :2 在左侧对象资源处理器中找到根节点,也就是你安装sqlserver时注册的服务器名称.然 ...
- SQL Server 本地数据库登录不上 解决方法
sql本地数据库登录不了的话.先看看自己计算机 服务 SQL server (MSSQLSERVER) 没有打开的话,请打开. 今天说的情景模式是 你误删了windows登录:禁用了sa登录:s ...
- 看看如何解决“SQL Server只能使用Windows身份登录,不能使用sa等Sql server身份进行登录”的问题
今天安装Sql Server之后,出现SQL Server只能使用Windows身份登录,不能使用sa等Sql server身份进行登录的问题是由于sql server只设置了Windows身份验证, ...
- SQL Server 2008 错误15023:当前数据库中已存在用户或角色
解决SQL Server 2008 错误15023:当前数据库中已存在用户或角色,SQLServer2008,错误15023,在使用SQL Server 2008时,我们经常会遇到一个情况:需要把一台 ...
- 转:Sql Server中清空所有数据表中的记录
如果要删除数据表中所有数据只要遍历一下数据库再删除就可以了,清除所有数据我们可以使用搜索出所有表名,构造为一条SQL语句进行清除了,这里我一一给各位同学介绍. 使用sql删除数据库中所有表是不难的 ...
- Sql Server中清空所有数据表中的记录
Sql Server中清空所有数据表中的记录 清空所有数据表中的记录: 代码如下:exec sp_msforeachtable @Command1 ='truncate table ?'删除所有数据 ...
- 如何在Sql Server中读取最近一段时间的记录,比如取最近3天的或最近3个月的记录。
如何在Sql Server中读取最近一段时间的记录,比如取最近3天的或最近3个月的记录. 主要用到DATEADD函数,下面是详细语句 取最近3天 select * from 表名where rq> ...
- 慎重管理SQL Server服务的登录(启动)账户和密码
今天是大年初三,先跟大家拜个年,祝大家新年快乐.今天处理了一个alwaysOn问题——辅助副本因为磁盘空间不足一直显示[未同步——可疑],在日志中可以看到数据库处于挂起状态,与主副本失去同步.原以为只 ...
随机推荐
- jdk1.6与Myeclipse的冲突造成的
出现这样的错误时:ERROR:JDWP Unable to get JNI 1.2 environment ,jvm-> GetEvn() return =- ...
- mysql学习笔记 第六天
改变数据表的结构: alter table tb_name action,[action,action](使用alter table 之前,需要查看数据表的当前定义,需要执行show create t ...
- 解决ambiguous symbol命名空间中类名、变量名冲突的问题
最近在将一个复杂的工程集成到现有的项目中.编译时发现,有的变量名冲突了,提示就是xxxx ambiguous symbol,并且在编译输出时,指明了两个文件当中特定的变量名或者类名相同.出现这个编译错 ...
- Java集合源码分析(六)TreeSet<E>
TreeSet简介 TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, j ...
- PHP替代正则匹配的高效函数
strpos() - 查找字符串首次出现的位置 strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写). strripos() 函数查找字符串在另一字符串中最后一次出现的位置 ...
- Android 亮度调节
最近在做一个App的设置项,亮度调节.真正做时,发现Android亮度调节比预想要复杂一些.其实目前网上已有不少这方面的资料,但有些博文具有一定误导性.在此将这块内容按照自己理解整理一下. 整体上看, ...
- css通用小笔记02——浮动、清除(三个例子)
css中通常会用到浮动与清除,也是一个必须掌握的知识点,概念性的东西不多说,下面举几个例子,来说明它的用法:1.文字环绕效果 2.多个div并排显示 3.清除浮动(默认显示) 一.文字环绕效果: h ...
- Javascript的历史
阅读了JavaScript dom简史,从网上看了下,学问很深啊. 首先简单说下网景公司(Netscape)的发展史:1993年,美国国家超级计算机应用中心(NCSA),发表了一个浏览器,命名为“Mo ...
- 用SVG绕过浏览器XSS审计
[Translated From]:http://insert-script.blogspot.com/2014/02/svg-fun-time-firefox-svg-vector.html === ...
- CoreDataManager-OC版-兼容iOS10以前的版本
头文件: #import <Foundation/Foundation.h> #import <CoreData/CoreData.h> /** CoreData管理器 */ ...