开发者

How to check if two Datetime variables (from and to) belongs to a specific range?

开发者 https://www.devze.com 2023-02-22 15:44 出处:网络
I Have a table which stores a log of user sessions in a machine. It has 3 columns which i would use. time_from, time_to and the username. What I was trying to do is to calculate the peak usage hours o

I Have a table which stores a log of user sessions in a machine. It has 3 columns which i would use. time_from, time_to and the username. What I was trying to do is to calculate the peak usage hours of that computer. For that I would get the total number of minutes used per hour and the number of users. I tried to get the durations of sessions which fall between a specific time range like time_from > 0:00 and time_to < 1:00 but that only worked for sessions less than one hour. in my table the开发者_开发问答re are sessions that go upto 6 hours and these sessions will go overnight. My basic requirement is to get the durations of the sessions that belong to each 24 hours in a speicific date but i haven't been able to deal with sessions that go over 1 hour in length. Is there any way i can get this through purely using SQL or would i have to deal it with another program?

I'm using ms sql server btw.


Basically, my solution entails building a calendar of all hours between your start datetime and end datetime, then calculating the amount of minutes during each hour interval and finally totally by interval.

Declare @StartDate datetime
Declare @EndDate datetime

Set @StartDate = ...
Set @EndDate = ...

;With Calendar As
    (
    Select 1 As Num, DateAdd(d, DateDiff(d, 0, @StartDate), 0) As [Date]
    Union All
    Select Num + 1, DateAdd(hh, 1, [Date])
    From Calendar
    Where [Date] <= @EndDate
    )
    , Ranges As
    (
    Select C1.Num, C1.Date As StartDate, Coalesce(C2.Date, @EndDate) As EndDate
    From Calendar As C1
        Left Join Calendar As C2
            On C2.[Num] = C1.Num + 1
    )
    , UsageByHour As
    (
    Select U.username, R.Num As RangeNum, DateDiff( mi, R.StartDate, U.time_to ) As UsageMinutes
    From user_sessions As U
        Join Ranges As R
            On U.time_from Between R.StartDate And R.EndDate
                And U.time_to <= R.EndDate
    Union All       
    Select U.username, R.Num, DateDiff( mi, U.time_from, R.EndDate )
    From user_sessions As U
        Join Ranges As R
            On U.time_from Between R.StartDate And R.EndDate
                And U.time_to > R.EndDate
    Union All       
    Select U.username, R.Num, DateDiff( mi, R.StartDate, U.time_to )
    From user_sessions As U
        Join Ranges As R
            On U.time_to Between R.StartDate And R.EndDate
                And U.time_from < R.StartDate
    )
Select U.username, C.StartDate, C.EndDate, Sum( U.UsageMinutes )
From Usage As U
    Join Calendar As C
        On C.Num = U.RangeNum
Group By U.username, C.StartDate, C.EndDate
Option ( MaxRecursion 0 );

If you then wanted to find the peak minutes, I would put the above final query into another CTE and query against that:

...
    , UsageByInterval As
    (
    Select U.username, C.StartDate, C.EndDate, Sum( U.UsageMinutes ) As UsageMinutes
    From Usage As U
        Join Calendar As C
            On C.Num = U.RangeNum
    Group By U.username, C.StartDate, C.EndDate
    )
    , UsageRanking As
    (
    Select U.username, C.StartDate, C.EndDate, UsageMinutes
        , Row_Number() Over( Partition By U.username 
                                            Order By UsageMinutes Desc ) As Rnk
    From UsageByInterval
    )
Select username, StartDate, EndDate, UsageMinutes
From UsageRanking
Where Rnk = 1


Make a table of intervals and then:

SELECT interval
  , count(*) as sessions
  , count(distinct username) as users
  , SUM(DATEDIFF(s, time_to, time_from)) as seconds_used
FROM (
    SELECT user_sessions.username
      , intervals.interval
      , CASE
          WHEN intervals.time_from < user_sessions.time_from
          THEN user_sessions.time_from
          ELSE intervals.time_from
        END as time_from
      , CASE
        CASE
          WHEN user_sessions.time_to < intervals.time_to
          THEN user_sessions.time_to
          ELSE intervals.time_to
        END as time_from
    FROM user_sessions
      JOIN intervals
        ON intervals.time_from < user_sessions.time_to
          AND user_sessions.time_from < intervals.time_to
  )
GROUP BY interval;

Note that I don't use MS SQL server so I made this SQL generic and fairly portable. There may be a more compact way to do this. I also didn't test, so there may be obvious (hopefully easily fixed) bugs.

0

精彩评论

暂无评论...
验证码 换一张
取 消