业务需求描述:

有一个用户登录表,用户每次登陆时,就会向这个表中插入一条数据,这个表记录了用户的用户ID和登录时间,表的数据量有几千万,

现在需要求出从今天开始算,用户持续登录的时间(也就是用户今天登陆了,昨天也登陆了,但是前天没有登录,那用户的持续登录时间就

是一天)。

分析:

看似蛮简单的一需求,在数据库里面实际操作起来不是那么简单的,并非一个简单的Select能够搞定的,从业务的描述我们起码可以得到如下

的分析结论:

1. 业务需要统计这样的数据,应该并不需要实时的数据,所以我们可以获取某个时间的快照数据来做计算;

2. 表数据量比较大,如果直接在这个表上操作,势必会对产品的使用造成影响(因为每个用户登录时,都需要再往里面插数据的);

3. 需要统计每个用户的持续登录时间,那意味着如果没有持续登录时间的用户就是不需要的用户,这里面应该可以筛选掉一大批用户;

4. 每个用户都需要做统计计算,这个肯定是一个循环计算的过程,我们最好前期能过滤掉一部分用户,那后面的统计计算无疑可以节省很多的时间;

5. 用户持续登录,一般时间不可能很长(很少有人天天去登陆一个网站,持续100天的吧),意味着我们可以用持续天数来做为循环条件,

而不必以用户来作为循环条件(用户做循环条件的话,循环次数应该会比较大);

6. 大数据量做统计,而且是定位到每个用户的,性能是必须要重点考虑的因素;

造测试数据:

        测试数据其实有一个比较难的要求是能够尽量的接近真实数据,这样的测试效果才是最好的;我们预计造一个2千5百万的表,造几十万的用户,然后随机

的生成登陆时间,但是要求登录时间尽量能贴合真实的情况;

我们先创建测试表(其实最好是分区表):

--Create Test Tablecreate table dbo.UserLoginInfo(     userid int    ,logintime datetime    ,CONSTRAINT [PK_UserInfo] PRIMARY KEY CLUSTERED     (          userid ASC        ,logintime ASC     )WITH     (         PAD_INDEX  = OFF        ,STATISTICS_NORECOMPUTE  = OFF        ,IGNORE_DUP_KEY = OFF        ,ALLOW_ROW_LOCKS  = ON        ,ALLOW_PAGE_LOCKS  = ON        ,FILLFACTOR = 90    ) ON [PRIMARY] ) ON [PRIMARY]

--DST Tablecreate table dbo.DST_UserLoginInfo(     userid int    ,logindate varchar(10)    ,ContinueDays smallint    ,IsOver bit    ,CONSTRAINT [PK_UserLoginInfo] PRIMARY KEY CLUSTERED     (          userid ASC        ,logindate ASC     )WITH     (         PAD_INDEX  = OFF        ,STATISTICS_NORECOMPUTE  = OFF        ,IGNORE_DUP_KEY = OFF        ,ALLOW_ROW_LOCKS  = ON        ,ALLOW_PAGE_LOCKS  = ON        ,FILLFACTOR = 90    ) ON [PRIMARY] ) ON [PRIMARY]

说明:我们创建了两个表,一个是模拟真实的用户登录数据的表,另外一个是我们准备做数据统计的表;

我们按时间由远到近,分几个批次来生成测试数据:

/*-----------造二千五百万用户登录记录的过程---------------------*/--第一批次set nocount ongo--1000*5000=500Wdeclare @u_count intset @u_count=0--1000while @u_count<1000begindeclare @userid int,@count intset @userid= rand()*100000set @count=0--5000while @count<5000begininsert into dbo.UserLoginInfo(userid,logintime)--4000select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%4000),getdate()-4000),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

--第二批次set nocount ongo--1000*6000=600Wdeclare @u_count intset @u_count=0while @u_count<1000begindeclare @userid int,@count intset @userid= rand()*1000000set @count=0while @count<6000begin--3000insert into dbo.UserLoginInfo(userid,logintime)select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%3000),getdate()-3000),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

--第三批次set nocount ongo--1000*4000=400Wdeclare @u_count intset @u_count=0while @u_count<1000begindeclare @userid int,@count intset @userid= rand()*1000000set @count=0while @count<4000begin--2000insert into dbo.UserLoginInfo(userid,logintime)select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%2000),getdate()-2000),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

--第四批次set nocount ongo--10000*700=700Wdeclare @u_count intset @u_count=0while @u_count<10000begindeclare @userid int,@count intset @userid= rand()*100000000--select @useridset @count=0while @count<700begin--1000insert into dbo.UserLoginInfo(userid,logintime)select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%1000),getdate()-1000),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

--第五批次set nocount ongo--100000*10=100Wdeclare @u_count intset @u_count=0while @u_count<100000begindeclare @userid int,@count intset @userid= rand()*100000000set @count=0while @count<10begin--500insert into dbo.UserLoginInfo(userid,logintime)select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%500),getdate()-500),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

--第六批次set nocount ongo--100000*20=200Wdeclare @u_count intset @u_count=0while @u_count<100000begindeclare @userid int,@count intset @userid= rand()*100000000set @count=0while @count<20begin--365insert into dbo.UserLoginInfo(userid,logintime)select @userid,CAST(convert(varchar(10),dateadd(d,abs(checksum(newid())%365),getdate()-365),120)+' '+ str(5+abs(checksum(newid())%6),2)+':'+replace(str(abs(checksum(newid())%60),2),'','0')+':'+replace(str(abs(checksum(newid())%60),2),'','0') as datetime)set @count=@count+1endset @u_count=@u_count+1endgoset nocount off

造这些数据,在我本机上花了大半天时间才完成(痛苦呀),这些脚本运行完之后,我本机生成的数据量情况如下:

用户数量:

随机生成的时间情况:

现在我们完成了一个两千五百万记录,用户数量二十二万,时间从2001-03-05到2012-02-15的用户登录记录;但是有个问题,就是今天的时间,

没有加上去,我们再补充一下今天登陆的记录(假定今天每个用户都登陆了)

--add today login recode for every userinsert into dbo.UserLoginInfo(userid,logintime)select userid,DATEADD(MINUTE,-20,GETDATE())  from (select  distinct userid from dbo.UserLoginInfo with(nolock)) a

加完之后就应该有今天的数据了:

到这里造数据的过程就完了。

持续天数计算:

思路:1. 我们将UserLoginInfo的记录先做筛选,筛选掉那些今天有登陆,但是昨天没有登录的用户(也就是没有持续登录的用户);

2. 将筛选后的数据放入到中间表,我们通过中间表来计算用户持续登录的天数;

3. 计算完成后,通过查询中间表,输出用户和持续登录天数;

以下就按照前面的思路来开展步骤:

1. 筛选掉不需要的记录,并将其导入中间表:

--find data into other tableinsert into dbo.DST_UserLoginInfo(userid,logindate,ContinueDays,IsOver)select distinct userid,convert(varchar(10),logintime,23) as logindate,0,0from  dbo.UserLoginInfo a with(nolock) where exists--login today( select 1 from dbo.UserLoginInfo b with(nolock) where b.userid=a.useridand b.logintime<=GETDATE() and b.logintime>=convert(varchar(10),GETDATE(),23))--login yesterdayand exists(select 1 from dbo.UserLoginInfo c with(nolock) where a.userid=c.useridand c.logintime<convert(varchar(10),GETDATE(),23) and c.logintime>=convert(varchar(10),GETDATE()-1,23))

说明:在这里我们将时间字段变成了日期型的字符串,方便后面计算时做判断,另外中间表增加了持续时间和标识位字段,也是为了方便后面的计算。

2. 通过天数来循环中间表,计算持续天数:
   我们先来看一下,筛选完成后的数据量为800多万,节省了持续天数计算时大量的计算量;

接着我们来计算持续天数:

这里有两种方式:

1. 按用户来循环计算:

每次取一个用户,然后根据用户ID来循环计算这个用户的持续登录天数;但是设想一下,如果我们有10万个用户,那我们第一层次取用户的循环将要

循环10万次,而且每个用户又需要在第一层的循环里面做持续时间天数的循环计算,可以想象计算量是非常大的,不可取;

2. 按持续天数来循环计算:

前面分析阶段已经提及过,正常情况下很少有用户能持续100天,每天都登陆到一个网站上面的,那意味着我们循环的天数不会是一个非常大的量,而

且这种循环一次性的将同一个持续天数的用户一次性计算完成了,效率应该是比较高的,我们采用这种方式来进行计算;

--Update ContinueDaysdeclare @days smallint set @days=1while exists (select 1 from dbo.DST_UserLoginInfo where IsOver=0)beginupdate a set ContinueDays=@days,IsOver=1from dbo.DST_UserLoginInfo awhere IsOver=0and exists  (select 1 from dbo.DST_UserLoginInfo b with(nolock) where a.userid=b.userid and IsOver=0and b.logindate=convert(varchar(10),GETDATE()-@days,23))and not exists  (select 1 from dbo.DST_UserLoginInfo c with(nolock) where a.userid=c.userid and IsOver=0and c.logindate=convert(varchar(10),GETDATE()-@days-1,23))

set @days=@days+1end

运行完成后,我们来查看下运行的结果:

一共是14639个用户有持续登录,最长的登录时间为259天(真实情况应该不会有这么大,计算的时候最耗时的就是这两个持续时间大的用户)。

  验证结果:

我现在来抽查几条数据,看是否正确:

先在中间表中随便找一个持续时间为一天的记录,再到原登录表中找到他实际的登录数据:

打钩的是持续时间,正好是一天(其实这里的理解好像有点问题,用户明明是连续两天登录,而此处的持续时间只算做一天);

接下来抽个三天的记录:

结果也是正确的。

总结:

分析、造数据、测试和计算我们都做完了,现在将上面的计算过程做成一个存储过程,这样就方便随时调用了:

Create Proc usp_UserContinueDaysasbeginset nocount on--truncate dst tableif OBJECT_ID('dbo.DST_UserLoginInfo') is nullbegin--DST Table    create table dbo.DST_UserLoginInfo    (         userid int        ,logindate varchar(10)        ,ContinueDays smallint        ,IsOver bit        ,CONSTRAINT [PK_UserLoginInfo] PRIMARY KEY CLUSTERED         (              userid ASC            ,logindate ASC         )WITH         (             PAD_INDEX  = OFF            ,STATISTICS_NORECOMPUTE  = OFF            ,IGNORE_DUP_KEY = OFF            ,ALLOW_ROW_LOCKS  = ON            ,ALLOW_PAGE_LOCKS  = ON            ,FILLFACTOR = 90        ) ON [PRIMARY]     ) ON [PRIMARY]

endelsetruncate table dbo.DST_UserLoginInfo

--find data to other tableinsert into dbo.DST_UserLoginInfo(userid,logindate,ContinueDays,IsOver)select distinct userid,convert(varchar(10),logintime,23) as logindate,0,0from  dbo.UserLoginInfo a with(nolock) where exists--login today( select 1 from dbo.UserLoginInfo b with(nolock) where b.userid=a.useridand b.logintime<=GETDATE() and b.logintime>=convert(varchar(10),GETDATE(),23))--login yesterdayand exists(select 1 from dbo.UserLoginInfo c with(nolock) where a.userid=c.useridand c.logintime<convert(varchar(10),GETDATE(),23) and c.logintime>=convert(varchar(10),GETDATE()-1,23))

--Update ContinueDaysdeclare @days smallint set @days=1while exists (select 1 from dbo.DST_UserLoginInfo where IsOver=0)beginupdate a set ContinueDays=@days,IsOver=1from dbo.DST_UserLoginInfo awhere IsOver=0and exists  (select 1 from dbo.DST_UserLoginInfo b with(nolock) where a.userid=b.userid and IsOver=0and b.logindate=convert(varchar(10),GETDATE()-@days,23))and not exists  (select 1 from dbo.DST_UserLoginInfo c with(nolock) where a.userid=c.userid and IsOver=0and c.logindate=convert(varchar(10),GETDATE()-@days-1,23))

set @days=@days+1end

--Resultselect userid,MIN(logindate) as LoginDate ,ContinueDays  from dbo.DST_UserLoginInfo group by userid,ContinueDays order by ContinueDays desc

set nocount offend

到此,测试计算的过程完成,不过有点遗憾的是,这个业务需求是另外一个公司的朋友提供的,我没办法拿到他们原始的计算方法,所以就没用办法比较

算法的最终效果了;如果大家有更好的方法,欢迎讨论。

转载于:https://www.cnblogs.com/fygh/archive/2012/02/17/2354544.html

讨论一个比较有意思的业务需求相关推荐

  1. 清结算内部勾兑业务一个比较有意思的问题整理

    有段时间没有在博客上发表过内容了,都写在了文档上了,有时间全都整理发表出来,可以让应聘Java高工的同学看下. 今天遇到一个比较有意思的事情给大家说下,有关清结算内部勾兑的事情,你看完之后估计就会学到 ...

  2. WebRTC:应用中最大难点在于根据业务需求的适当折中

    WebRTC对主流PC浏览器.移动端的全覆盖,对于开发者而言无疑是一剂强心针,而在去年W3C大会上又提出通过QUIC来实现WebRTC.但在实际应用.行业契合以及对H.265的支持依然存在着不可忽视的 ...

  3. spring boot / cloud (二十) 相同服务,发布不同版本,支撑并行的业务需求

    有半年多没有更新了,按照常规剧本,应该会说项目很忙,工作很忙,没空更新,吧啦吧啦,相关的话吧, 但是细想想,是真的么?,忙到这几个字都没时间打么?毕竟大家都很忙的,所以忙并不是啥理由. 那是因为啥呢? ...

  4. 【数据挖掘】-模型怎么解决业务需求(五)

    目录 模型的保存 模型的优化 离线应用还是在线应用? 一个简单部署方案 记录项目经历,学会总结和反思 多考虑一点,如何适合更多场景 监控与迭代 模型的监控 重新开启 总结 我们的目标是业务需求,而数据 ...

  5. 五步法搞定BI业务需求梳理

    五步法搞定BI业务需求梳理.高手就是把复杂的事情简单化,简单的东西重复做.认真做. 五步法是哪五步 第一, 明确用户.商业智能BI项目的规划一切以用户需求为导向,首先需要明确各层次的需求用户.用户都不 ...

  6. 反射 -- 业务需求:执行某个类中全部的以test为开头的无参数无返回值的非静态方法。...

    package demo; //业务需求:执行某个类中全部的以test为开头的无参数无返回值的非静态方法. import java.lang.reflect.Method; import java.u ...

  7. 一个想法--开发与业务,我们互相依赖

    一个想法--开发与业务,我们互相依赖 编程之道很多人都读了,这位大师真是会利用中国阴阳学,但是中心思想就是工具就是工具,不是思想,经常听人们说,think in 什么,也是,如果单抱着什么编程实例也没 ...

  8. TOGAF:从业务架构到业务需求

    本文内容更新版本已转至  http://www.zhoujingen.cn/blog/3695.html -------------------------- 做管理型软件产品一般都要经历架构阶段,而 ...

  9. 软件需求包括3个不同的层次 - 业务需求、用户需求和功能需求

    首先有用户需求,然后由组织将用户需求转化为业务需求,再由开发者将业务需求转化为功能需求,功能需求映射到系统功能模块.业务需求也有可能是基于的业务发展需要,由组织首先提出来的. 业务需求(Busines ...

最新文章

  1. 文本分类中的一些小问题
  2. 科研文献|根相关真菌群落反映了亚热带森林中宿主的空间共生模式
  3. Gradle学习系列之十——自定义Plugin(本系列完)
  4. 【调参实战】如何开始你的第一个深度学习调参任务?不妨从图像分类中的学习率入手。...
  5. JZOJ 5640. 【NOI2018模拟4.9】劈配
  6. 2007-3-31第五天CCNA课
  7. 黑马ee在职进阶视频_进阶– Java EE 7前端5强
  8. led显示屏控制卡接线图解_Led显示屏出现花屏是什么原因
  9. CoreAnimation (CALayer 动画)
  10. Windows+Chrome OS双系统安装方法
  11. linux各版本的发布时间,linux服务器-红帽企业Linux各个版本发布时间和内核版本...
  12. MySql 入门.md
  13. InvalidClassException
  14. 【渝粤教育】国家开放大学2018年春季 0242-21T机械制图 参考试题
  15. 计算机奖状模板,制作电子奖状
  16. 高手教你如何从零开始学游戏编程
  17. Python-批处理修改音频采样率(批量重采样)
  18. 第九课堂: 基于兴趣、技能和经验分享的网络交易平台
  19. 图解:什么是拓扑排序?
  20. grpc加TLS加密和令牌认证

热门文章

  1. 设置linearlayout最大高度_ICEM CFD网格设置参数意义
  2. 如何打造园本特色_如何打造一个可持续发展的特色观光园?
  3. wamp惯用的php框架_wamp集成环境php多版本搭建(php5.5,php5.6,php7.0.6)
  4. c语言if判断文件_例8:C语言实现输入一个数,输出相应result
  5. JDK 5、6、7、8、9、10、11、12、13、14 新特性汇总
  6. vscode open in browser 默认浏览器
  7. PL-VIO论文阅读
  8. spss文件 服务器登录,spss连接远程服务器
  9. html语言代码游戏,常用html语言代码
  10. c打开指定路径文件_Selenium 系列篇(五):文件篇