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问题——辅助副本因为磁盘空间不足一直显示[未同步——可疑],在日志中可以看到数据库处于挂起状态,与主副本失去同步.原以为只 ...
随机推荐
- PHP的PSR系列规范都有啥内容
PSR 是PHP Standard Recommendation的简写,它其实应该叫PSRs,即系列推荐标准:目前通过的规范有PSR-0(Autoloading Standard).PSR-1(Bas ...
- 通俗易懂地讲解TCP建立连接的三次握手和释放连接的四次挥手
TCP建立连接时,为什么要进行三次挥手? 每一次TCP连接都需要三个阶段:连接建立.数据传送和连接释放.三次握手就发生在连接建立阶段. 在谢希仁著<计算机网络>第四版中讲三次握手的目的是为 ...
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- jQuery Portamento 滑动定位
版本: jQuery v1.3.2+ jQuery Portamento v1.1.1 注意事项: 在不支持position:fixed的浏览器里效果不是很好(例如 IE6.iOS4),可以在参数中设 ...
- MaterialUp - 寻找材料设计灵感必备的网站
MaterialUp 是一个展示最好的材料设计 APP,网站和概念的地方,每天都会更新.每款设计都精心挑选,展示那些有才华的设计师的伟大工作. MaterialUp 的核心价值是让尽可能多的设计师提供 ...
- 【2015上半年总结】js开源组件开发系列索引
js开源组件开发系列一索引 2015.8 by 田想兵 个人网站 从3月份进入新公司以来,时经五个月,我以平均每周1个小组件的速度,已经完成的js组件有22个之余了,已基本上全部用到实际项目中,这些小 ...
- mvc model 传值两种方式区别
1: controller中: public actionresult index() { M m=new M(); return view(m) } view中: @model.phone vs 中 ...
- TI的DSP、ST的ARM、Intel的X86浮点性能对比
估计没什么价值,单纯地记录下时间,以便以后查看. TMS320F28335 STM32f030 i3 4170 i3 4170 主频 150MHz 48MHz 3.7GHZ 3.7GHZ IDE ...
- Sql Server Always On主库与附库遇到的问题
使用Always On的时候永远要记住只有一个主数据库可写,如果写的话要不在监听节点上做写的操作,要不只在主数据库上写的操作不然只读库无法读写
- SharePoint 2013 为用户组自定义EventReceiver
前 言 在SharePoint的开发中,EventReceiver是很重要的一个部分,但是,常常遇到有些需要事件的时候,却没有相应的模板,因为EventReceiver创建时的模板只有那几个,除此之外 ...