Home > Net >  Calculate Concurrent logins for app (Oracle DB)
Calculate Concurrent logins for app (Oracle DB)

Time:01-31

I am not an Oracle guru but I would like to work out the highest concurrent logins for the day for an application based on the user's Start Time and Finish Time with distinct users. Below is an example and the table.

Table scheme:

Username Login Activity Start Date Finish Date
User1 NormalTime 2022-12-26T15:19:41 2022-12-26T15:19:44
User2 AfterHours 2023-01-09T01:29:02 2023-01-09T02:29:29
User3 NormalTime 2023-01-09T14:51:36 2023-01-09T16:28:17
User4 AfterHours 2023-01-10T04:38:41 2023-01-10T05:59:33
User5 NormalTime 2023-01-10T12:05:52 2023-01-10T13:08:29
User4 AfterHours 2023-01-26T04:45:51 2023-01-26T06:05:38
User4 AfterHours 2022-11-08T04:38:19 2022-11-08T05:48:58
User4 AfterHours 2022-11-09T04:40:03 2022-11-09T06:12:10
User4 AfterHours 2023-01-18T05:08:48 2023-01-18T06:20:27
User4 AfterHours 2023-01-20T05:40:05 2023-01-20T05:40:05
User1 NormalTime 2022-12-26T15:19:44 2022-12-26T16:20:09
User2 AfterHours 2023-01-09T02:47:24 2023-01-09T03:48:13
User4 AfterHours 2023-01-10T04:43:55 2023-01-10T06:00:15
User4 AfterHours 2023-01-23T05:21:35 2023-01-23T06:50:33
User4 AfterHours 2023-01-24T05:14:31 2023-01-24T06:24:04

I would like to return something like this

Table scheme:

Date Max Concurrent Logins
2022-12 2
2023-01 3

Example Situation

an example is there was a login at between 2023-01-30 6:00 and 23-01-30 7:00, then there is another user who logs in at 6:05 now there are 2 concurrent logins, and that same user logs out at 6:15 decreasing by 1 and 3rd user logs in at 6:20 and a 4th at 6:30 now there are 3 concurrent users. User 4 logs out at 6:45 and the 3rd user stays logged in by the time the 1st user logs out at 7:00 so the highest value for that time period is 3 concurrent users. and it would repeat that for the 3rd user who is still logged in. Hope this makes sense.

CodePudding user response:

You can count per each day firstly within the subquery, and then pick the maximum values within the main query such as

WITH t1 AS
(
 SELECT TO_CHAR(Finish_Date,'yyyy-mm') AS "Date", --> Date should be quoted as a reserved keyword 
        COUNT(*) OVER (PARTITION BY TRUNC(Finish_Date)) AS cnt
   FROM t -- your table
)   
SELECT "Date", MAX(cnt) AS "Max Concurrent Logins"
  FROM t1  
 GROUP BY "Date"
 ORDER BY 1

where I observe that the Start_Date and Finish_Date has the identical day values per each line.

CodePudding user response:

Used a bit changed sample data (to test chained concurrencies):

WITH
    logins (USERNAME, LOGIN_ACTIVITY, START_DATE, FINISH_DATE) AS
        (
            Select 'User1', 'NormalTime', To_Date('2022-12-26 15:19:41', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2022-12-26 15:19:44', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            
            Select 'User2', 'AfterHours', To_Date('2023-01-09 01:29:02', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-09 02:29:29', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User3', 'NormalTime', To_Date('2023-01-09 01:30:36', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-09 01:37:17', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User1', 'AfterHours', To_Date('2023-01-09 01:32:24', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-09 01:48:13', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User3', 'NormalTime', To_Date('2023-01-09 14:51:36', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-09 16:28:17', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User3', 'NormalTime', To_Date('2023-01-09 16:27:36', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-09 17:00:17', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            
            Select 'User4', 'AfterHours', To_Date('2023-01-10 04:38:41', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-10 05:59:33', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User5', 'NormalTime', To_Date('2023-01-10 12:05:52', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-10 13:08:29', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2023-01-26 04:45:51', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-26 06:05:38', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2022-11-08 04:38:19', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2022-11-08 05:48:58', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2022-11-09 04:40:03', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2022-11-09 06:12:10', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2023-01-18 05:08:48', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-18 06:20:27', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2023-01-20 05:40:05', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-20 05:40:05', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User1', 'NormalTime', To_Date('2022-12-26 15:19:44', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2022-12-26 16:20:09', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            
            Select 'User3', 'AfterHours', To_Date('2023-01-10 04:43:55', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-10 06:00:15', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2023-01-23 05:21:35', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-23 06:50:33', 'yyyy-mm-dd hh24_mi:ss') From Dual Union All
            Select 'User4', 'AfterHours', To_Date('2023-01-24 05:14:31', 'yyyy-mm-dd hh24_mi:ss'), To_Date('2023-01-24 06:24:04', 'yyyy-mm-dd hh24_mi:ss') From Dual
        ),

Create CTE (grid) to put previous and next row data in every row:

  grid AS
    (
        Select
            ROW_NUMBER() OVER(Order By START_DATE) "RN",
            LAG(USERNAME) OVER(Order By START_DATE) "PREV_USERNAME",
            USERNAME,
            LEAD(USERNAME) OVER(Order By START_DATE) "NEXT_USERNAME",
            --
            LAG(START_DATE) OVER(Order By START_DATE) "PREV_START_DATE",
            START_DATE,
            LEAD(START_DATE) OVER(Order By START_DATE) "NEXT_START_DATE",
            --
            LAG(FINISH_DATE) OVER(Order By START_DATE) "PREV_FINISH_DATE",
            FINISH_DATE,
            LEAD(FINISH_DATE) OVER(Order By START_DATE) "NEXT_FINISH_DATE"
        From
            logins
        Order By START_DATE
    )

G r i d :
        RN PREV_USERNAME USERNAME NEXT_USERNAME PREV_START_DATE START_DATE NEXT_START_DATE PREV_FINISH_DATE FINISH_DATE NEXT_FINISH_DATE
---------- ------------- -------- ------------- --------------- ---------- --------------- ---------------- ----------- ----------------
         1               User4    User4                         08-NOV-22  09-NOV-22                        08-NOV-22   09-NOV-22        
         2 User4         User4    User1         08-NOV-22       09-NOV-22  26-DEC-22       08-NOV-22        09-NOV-22   26-DEC-22        
         3 User4         User1    User1         09-NOV-22       26-DEC-22  26-DEC-22       09-NOV-22        26-DEC-22   26-DEC-22        
         4 User1         User1    User2         26-DEC-22       26-DEC-22  09-JAN-23       26-DEC-22        26-DEC-22   09-JAN-23        
         5 User1         User2    User3         26-DEC-22       09-JAN-23  09-JAN-23       26-DEC-22        09-JAN-23   09-JAN-23        
         6 User2         User3    User1         09-JAN-23       09-JAN-23  09-JAN-23       09-JAN-23        09-JAN-23   09-JAN-23        
         7 User3         User1    User3         09-JAN-23       09-JAN-23  09-JAN-23       09-JAN-23        09-JAN-23   09-JAN-23        
         8 User1         User3    User3         09-JAN-23       09-JAN-23  09-JAN-23       09-JAN-23        09-JAN-23   09-JAN-23        
         9 User3         User3    User4         09-JAN-23       09-JAN-23  10-JAN-23       09-JAN-23        09-JAN-23   10-JAN-23        
        10 User3         User4    User3         09-JAN-23       10-JAN-23  10-JAN-23       09-JAN-23        10-JAN-23   10-JAN-23        
        11 User4         User3    User5         10-JAN-23       10-JAN-23  10-JAN-23       10-JAN-23        10-JAN-23   10-JAN-23        
        12 User3         User5    User4         10-JAN-23       10-JAN-23  18-JAN-23       10-JAN-23        10-JAN-23   18-JAN-23        
        13 User5         User4    User4         10-JAN-23       18-JAN-23  20-JAN-23       10-JAN-23        18-JAN-23   20-JAN-23        
        14 User4         User4    User4         18-JAN-23       20-JAN-23  23-JAN-23       18-JAN-23        20-JAN-23   23-JAN-23        
        15 User4         User4    User4         20-JAN-23       23-JAN-23  24-JAN-23       20-JAN-23        23-JAN-23   24-JAN-23        
        16 User4         User4    User4         23-JAN-23       24-JAN-23  26-JAN-23       23-JAN-23        24-JAN-23   26-JAN-23        
        17 User4         User4                  24-JAN-23       26-JAN-23                  24-JAN-23        26-JAN-23               

Main SQL

Select
    TRUNC(START_DATE) "CONCURRENCY_DATE",
    Count(CONCURRENCY) "MAX_CONCURRENT_USERS"
From
    (    SELECT    RN,
                  USERNAME,
                  START_DATE,
                  CASE  WHEN  USERNAME != PREV_USERNAME And Trunc(START_DATE) = Trunc(PREV_START_DATE) And 
                              START_DATE Between PREV_START_DATE And PREV_FINISH_DATE
                        THEN  1
                        WHEN  USERNAME != NEXT_USERNAME And Trunc(START_DATE) = Trunc(NEXT_START_DATE) And 
                              NEXT_START_DATE Between START_DATE And FINISH_DATE
                        THEN  1
                  END "CONCURRENCY"
        FROM
            grid g
        ORDER BY RN
    )
Where CONCURRENCY Is Not Null
Group By TRUNC(START_DATE)
Order By TRUNC(START_DATE)

R e s u l t :

CONCURRENCY_DATE MAX_CONCURRENT_USERS
09-JAN-23 3
10-JAN-23 2

With a differrent grid and detailed main SQL you could have insight to the concurrecies:

  grid AS
    (
        Select
            ROW_NUMBER() OVER(Order By START_DATE) "RN",
            LAG(USERNAME) OVER(Order By START_DATE) "PREV_USERNAME",
            USERNAME,
            LEAD(USERNAME) OVER(Order By START_DATE) "NEXT_USERNAME",
            --
            LAG(START_DATE) OVER(Order By START_DATE) "PREV_START_DATE",
            START_DATE,
            LEAD(START_DATE) OVER(Order By START_DATE) "NEXT_START_DATE",
            --
            LAG(START_DATE) OVER(Order By START_DATE) - LAG(Trunc(START_DATE)) OVER(Order By START_DATE) "PREV_START_DAY_TIME",
            START_DATE - Trunc(START_DATE) "START_DAY_TIME", 
            LEAD(START_DATE) OVER(Order By START_DATE) - LEAD(Trunc(START_DATE)) OVER(Order By START_DATE) "NEXT_START_DAY_TIME",
            --
            LAG(FINISH_DATE) OVER(Order By START_DATE) "PREV_FINISH_DATE",
            FINISH_DATE,
            LEAD(FINISH_DATE) OVER(Order By START_DATE) "NEXT_FINISH_DATE",
            --
            Round(LAG(FINISH_DATE) OVER(Order By START_DATE) - LAG(Trunc(FINISH_DATE)) OVER(Order By START_DATE), 9) "PREV_END_DAY_TIME",
            Round(FINISH_DATE - Trunc(FINISH_DATE), 9) "END_DAY_TIME",
            Round(LEAD(FINISH_DATE) OVER(Order By START_DATE) - LEAD(Trunc(FINISH_DATE)) OVER(Order By START_DATE), 9) "NEXT_END_DAY_TIME",
            Round(FINISH_DATE - START_DATE, 9) "LOGGED_DAY_TIME"
        From
            logins
        Order By START_DATE
    )
Select
    TRUNC(START_DATE) "CONCURRENCY_DATE",
    LPAD(FLOOR(CONCURRENCY_START_TIME*24*60*60/(60*60)), 2, '0') || ':' || 
    LPAD(FLOOR( (CONCURRENCY_START_TIME - FLOOR(CONCURRENCY_START_TIME*24*60*60/(60*60))/24) * 24 * 60 ), 2, '0') || ':' || 
    LPAD(FLOOR((((CONCURRENCY_START_TIME - FLOOR(CONCURRENCY_START_TIME*24*60*60/(60*60))/24) * 24 * 60) - 
    FLOOR( (CONCURRENCY_START_TIME - FLOOR(CONCURRENCY_START_TIME*24*60*60/(60*60))/24) * 24 * 60 ))*60), 2, '0') "CONCURRENCY_START_TIME",
    --
    LPAD(FLOOR(CONCURRENCY_END_TIME*24*60*60/(60*60)), 2, '0') || ':' || 
    LPAD(FLOOR( (CONCURRENCY_END_TIME - FLOOR(CONCURRENCY_END_TIME*24*60*60/(60*60))/24) * 24 * 60 ), 2, '0') || ':' || 
    LPAD(FLOOR((((CONCURRENCY_END_TIME - FLOOR(CONCURRENCY_END_TIME*24*60*60/(60*60))/24) * 24 * 60) - 
    FLOOR( (CONCURRENCY_END_TIME - FLOOR(CONCURRENCY_END_TIME*24*60*60/(60*60))/24) * 24 * 60 ))*60), 2, '0') "CONCURRENCY_END_TIME",
    CONCURRENT_USERS
From
    (
        SELECT    RN,
                  USERNAME,
                  START_DATE,
                  CASE  WHEN  USERNAME != PREV_USERNAME And Trunc(START_DATE) = Trunc(PREV_START_DATE) And 
                              START_DATE Between PREV_START_DATE And PREV_FINISH_DATE
                        THEN  PREV_USERNAME || ', ' || USERNAME
                        WHEN  USERNAME != NEXT_USERNAME And Trunc(START_DATE) = Trunc(NEXT_START_DATE) And 
                              NEXT_START_DATE Between START_DATE And FINISH_DATE
                        THEN  USERNAME || ', ' || NEXT_USERNAME
                  END "CONCURRENT_USERS",
                  --
                  CASE  WHEN  USERNAME != PREV_USERNAME And Trunc(START_DATE) = Trunc(PREV_START_DATE) And 
                              START_DATE Between PREV_START_DATE And PREV_FINISH_DATE
                        THEN  START_DAY_TIME
                        WHEN  USERNAME != NEXT_USERNAME And Trunc(START_DATE) = Trunc(NEXT_START_DATE) And 
                              NEXT_START_DATE Between START_DATE And FINISH_DATE
                        THEN  NEXT_START_DAY_TIME
                  END "CONCURRENCY_START_TIME",
                  --
                  CASE  WHEN  USERNAME != PREV_USERNAME And Trunc(START_DATE) = Trunc(PREV_START_DATE) And 
                              START_DATE Between PREV_START_DATE And PREV_FINISH_DATE
                        THEN  LEAST(END_DAY_TIME, PREV_END_DAY_TIME)
                        WHEN  USERNAME != NEXT_USERNAME And Trunc(START_DATE) = Trunc(NEXT_START_DATE) And 
                              NEXT_START_DATE Between START_DATE And FINISH_DATE
                        THEN  LEAST(NEXT_END_DAY_TIME, END_DAY_TIME)
                  END "CONCURRENCY_END_TIME",
                  NEXT_END_DAY_TIME
        FROM
            grid g
        ORDER BY RN
    )
Where CONCURRENT_USERS Is Not Null
Order By TRUNC(START_DATE)

R e s u l t :
CONCURRENCY_DATE CONCURRENCY_START_TIME CONCURRENCY_END_TIME CONCURRENT_USERS
---------------- ---------------------- -------------------- ----------------
09-JAN-23        01:30:36               01:37:16             User2, User3     
09-JAN-23        01:30:36               01:37:16             User2, User3     
09-JAN-23        01:32:24               01:37:16             User3, User1     
10-JAN-23        04:43:55               05:59:33             User4, User3     
10-JAN-23        04:43:55               05:59:33             User4, User3   
  • Related