按登录时间查找最大并发用户数

时间:2015-01-22 19:46:20

标签: sql sql-server sql-server-2008

我有一个表格可以保存用户的登录时间:

 | Login time       | Logout time      | System | Username
 | 01/01/2015 09:00 | 01/01/2015 10:00 | A      | Joe
 | 01/01/2015 10:00 | 01/01/2015 11:00 | A      | Fred
 | 01/01/2015 12:00 | 01/01/2015 14:00 | B      | Tom
 | 01/01/2015 15:30 | 01/01/2015 16:00 | B      | Dick
 | 01/01/2015 09:00 | 01/01/2015 17:00 | A      | Harry

...我想报告特定日期每个system的最大并发用户数:在上面的例子中,它是

 | System | Concurrency
 |   A    |   2
 |   B    |   1

...因为Fred和Harry同时登录了系统AB只有一个并发用户。

我试过了:

SELECT t1.system,count(t1.system) 
FROM logins t1,logins t2
WHERE t1.system=t2.system
and t2.logintime <= t1.logouttime
AND  t2.logouttime>t1.logintime
group by t1.system

......但这似乎没有给我我想要的东西。有人能提出最简单的解决方案吗?

3 个答案:

答案 0 :(得分:2)

在任何给定时间内,您可以通过在时间或之前累计登录次数并减去注销次数来获取并发用户数。

有几种方法可以解决这个问题。在SQL Server 2008中,一种相对简单的方法是使用相关子查询。因此,以下内容在任何给定时间获取累积值:

      select l.system, l.logintime,
             (select count(*)
              from logins l2
              where l2.system = l.system and l2.logintime <= l.logintime
             ) as cumeins,
             (select count(*)
              from logins l2
              where l2.system = l.system and l2.logouttime <= l.logintime
             ) as cumeouts
      from logins l
     ) l
group by l.system, cast(logintime as date);

要获得每天的最大数量,只需对上面的内容进行汇总并拉出最大差异:

  select system, cast(logintime as date) as thedate,
         max(coalesce(cumeins, 0) - coalesce(cumeouts, 0)) as concurrent
  from (select l.logintime,
               (select count(*)
                from logins l2
                where l2.system = l.system and l2.logintime <= l.logintime
               ) as cumeins,
               (select count(*)
                from logins l2
                where l2.system = l.system and l2.logouttime <= l.logintime
               ) as cumeouts
        from logins l
       ) l
  group by system, cast(logintime as date);

答案 1 :(得分:1)

Atilla对于如何定义并发是正确的。我们假设您将同时定义为同一分钟登录。在这种情况下,您需要以下内容(我的专栏名称可能与您的名称不同,因为习惯我不包含空格)。如果你针对你的数据运行它,它将得到你想要的。如果你想要最接近的5分钟或最近的毫秒(可能运行得非常慢),你需要相应地调整with语句中的内容,等等。如果你真的想要真正的并发,你需要一个相关子查询的组合与Gordon Linoff建议的相似但更复杂。

declare @minlogintime smalldatetime,@maxlogouttime smalldatetime
select @minlogintime=min(logintime),@maxlogouttime=max(logouttime) from logins;
with minuterange as
(
    select dt=dateadd(mi,1,@minlogintime)
    where dateadd(mi,1,@minlogintime) < @maxlogouttime
    union all 
    select dateadd(mi,1,dt)
    from minuterange
    where dateadd(mi,1,dt) < @maxlogouttime
)
select * into #tmpminutes from minuterange
option (maxrecursion 0)

--get concurrency count
select b.system,convert(varchar,b.dt,112),max(b.users)
from
(select a.system,a.dt,count(distinct a.username) as users
from
(SELECT
    t1.Username,t1.System,m.dt
FROM
    Logins t1
INNER JOIN #tmpminutes m on m.dt between t1.logintime and t1.logouttime) as a
GROUP BY
    a.System,a.dt) as b
GROUP BY
    b.System,convert(varchar,b.dt,112)

drop table #tmpminutes

答案 2 :(得分:0)

使用以下日期组,请参阅以下fiddle

SELECT System,convert(varchar(10),LoginTime,112),COUNT(*) AS CONCURRENT FROM logins
GROUP BY SYSTEM,convert(varchar(10),LoginTime,112)

如果您希望窗口比天更紧,则应使用不同的分组。

这并不能完全满足您的需求,但您需要更好地定义问题。你的并发窗口是什么?一小时或一天?

以下SQL提供了您定义的并发用户但没有关于并发窗口的更多信息,如何对这些并发用户进行分组和计数?

SELECT 
l1.System,convert(varchar(10),l1.LoginTime,120) AS l1LoginDay
, l1.Username as l1Username
, l2.Username as l2Username

FROM logins l1 
INNER JOIN logins l2
ON 
l1.System = l2.System
AND convert(varchar(10),l1.LoginTime,120) = convert(varchar(10),l2.LoginTime,120)
AND l1.Username != l2.Username
WHERE
1 = 1
and l2.LoginTime <= l1.LogoutTime
AND  l2.LogoutTime>l1.LoginTime