• subscribe
November 24, 2009 12:00 AM

More about Calculating Concurrent Sessions

SQL Server Pro
InstantDoc ID #103203

Recently I wrote a two-part series in my T-SQL Black Belt column about Calculating Concurrent Sessions (Part I – November 2009, Part II – December 2009). I used the following code to create a table called Sessions and populate it with sample data:

SET NOCOUNT ON;

USE tempdb;

 

IF OBJECT_ID('dbo.Sessions', 'U') IS NOT NULL DROP TABLE dbo.Sessions;

 

CREATE TABLE dbo.Sessions

(

  keycol    INT         NOT NULL,

  app       VARCHAR(10) NOT NULL,

  usr       VARCHAR(10) NOT NULL,

  host      VARCHAR(10) NOT NULL,

  starttime DATETIME    NOT NULL,

  endtime   DATETIME    NOT NULL,

  CONSTRAINT PK_Sessions PRIMARY KEY(keycol),

  CHECK(endtime > starttime)

);

GO

 

CREATE INDEX idx_nc_app_st_et ON dbo.Sessions(app, starttime, endtime);

CREATE INDEX idx_nc_app_et_st ON dbo.Sessions(app, endtime, starttime);

 

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(2,  'app1', 'user1', 'host1', '20090212 08:30', '20090212 10:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(3,  'app1', 'user2', 'host1', '20090212 08:30', '20090212 08:45');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(5,  'app1', 'user3', 'host2', '20090212 09:00', '20090212 09:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(7,  'app1', 'user4', 'host2', '20090212 09:15', '20090212 10:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(11, 'app1', 'user5', 'host3', '20090212 09:15', '20090212 09:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(13, 'app1', 'user6', 'host3', '20090212 10:30', '20090212 14:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(17, 'app1', 'user7', 'host4', '20090212 10:45', '20090212 11:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(19, 'app1', 'user8', 'host4', '20090212 11:00', '20090212 12:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(23, 'app2', 'user8', 'host1', '20090212 08:30', '20090212 08:45');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(29, 'app2', 'user7', 'host1', '20090212 09:00', '20090212 09:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(31, 'app2', 'user6', 'host2', '20090212 11:45', '20090212 12:00');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(37, 'app2', 'user5', 'host2', '20090212 12:30', '20090212 14:00');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(41, 'app2', 'user4', 'host3', '20090212 12:45', '20090212 13:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(43, 'app2', 'user3', 'host3', '20090212 13:00', '20090212 14:00');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(47, 'app2', 'user2', 'host4', '20090212 14:00', '20090212 16:30');

INSERT INTO dbo.Sessions(keycol, app, usr, host, starttime, endtime)

  VALUES(53, 'app2', 'user1', 'host4', '20090212 15:30', '20090212 17:00');

 

I presented a task to calculate the maximum number of concurrent sessions per each application and provided several solutions. I got great feedback from readers, including a beautiful new set-based solution that I will present in Part III in the series, which will probably be published in the March 2010 edition of SQL Server Magazine.

In the meanwhile I wanted to cover here a variation of the task that came up in a conversation I had with a fellow SQL Server MVP and SQL wizard, Peter Larsson (aka peso).

In Part II of the series I presented the following solution returning the maximum number of concurrent sessions per application:

-- Part 1

CREATE TABLE #Ends

(

  app       VARCHAR(10) NOT NULL,

  endtime   DATETIME,

  n         BIGINT

);

 

CREATE CLUSTERED INDEX idx_app_et ON #Ends(app, endtime);

 

INSERT INTO #Ends(app, endtime, n)

  SELECT app, endtime,

    RANK() OVER(PARTITION BY app ORDER BY endtime) AS n

  FROM dbo.Sessions;

 

-- Part 2

WITH Counts AS

(

  SELECT S.app, S.starttime,

    ROW_NUMBER() OVER(PARTITION BY S.app ORDER BY S.starttime)

      - A.n + 1 AS cnt

  FROM dbo.Sessions AS S

    CROSS APPLY (SELECT TOP (1) E.n

                 FROM #Ends AS E

                 WHERE E.app = S.app

                   AND E.endtime > S.starttime

                 ORDER BY E.endtime) AS A

)

SELECT app, MAX(cnt) AS mx

FROM Counts

GROUP BY app;

 

DROP TABLE #Ends;

 

As I described in the article, Part 1 of the solution populates a temporary table with ranked session end events. Part 2 assigns row numbers to session start events, and calculates the number of concurrent sessions at each start event by subtracting from the start event row number the rank of the next end event and adding 1. Finally, the outermost query groups the data by application, and returns the maximum count of concurrent sessions per each application.

In a conversation I had with Peter we discussed a variation of the problem where you also want to get back the start and end times of the actual intervals that appeared most often, and not just the max count. You can address this variation of the problem by applying a couple of revisions to the above solution to the original task. First, in the Counts CTE query return both the starttime and the endtime attributes so that you will be able to return information about the actual intervals in the outer query. Second, in the outer query use the option TOP (1) WITH TIES based on the ordering specification: ORDER BY RANK() OVER(PARTITION BY app ORDER BY cnt DESC). All intervals that occur most often per application will be ranked 1, hence this TOP specification will return all of them. Here’s the complete solution:

-- Part 1

CREATE TABLE #Ends

(

  app       VARCHAR(10) NOT NULL,

  endtime   DATETIME,

  n         BIGINT

);

 

CREATE CLUSTERED INDEX idx_app_et ON #Ends(app, endtime);

 

INSERT INTO #Ends(app, endtime, n)

  SELECT app, endtime,

    RANK() OVER(PARTITION BY app ORDER BY endtime) AS n

  FROM dbo.Sessions;

 

-- Part 2

WITH Counts AS

(

  SELECT S.app, S.starttime, A.endtime,

    ROW_NUMBER() OVER(PARTITION BY S.app ORDER BY S.starttime)

      - A.n + 1 AS cnt

  FROM dbo.Sessions AS S

    OUTER APPLY (SELECT TOP (1) E.endtime, E.n

                 FROM #Ends AS E

                 WHERE E.app = S.app

                   AND E.endtime > S.starttime

                 ORDER BY E.endtime) AS A

)

SELECT TOP (1) WITH TIES *

FROM Counts

ORDER BY RANK() OVER(PARTITION BY app ORDER BY cnt DESC);

 

DROP TABLE #Ends;

 

Note that in case there’s more than one interval that appears the maximum number of times, this solution will return all ties. If you want to return only one interval per application, use the ROW_NUMBER function instead of RANK, and add a tie breaker to the ROW_NUMBER’s ORDER BY list if you want the solution to be deterministic. As an example, if you want the earliest interval to win in case of ties, add starttime, endtime to the ORDER BY list, like so:

ORDER BY ROW_NUMBER() OVER(PARTITION BY app ORDER BY cnt DESC, starttime, endtime);

Cheers,

BG

 



ARTICLE TOOLS

Comments
  • ITZIK
    2 years ago
    Mar 05, 2010

    coulbournc: Yes, that's similar to the problem the article covers and the principal ideas in the code should work for your case. Of course, you would need to add the appropriate filters and adjust your indexes for optimal performance.

    Cheers,
    Itzik

  • George
    2 years ago
    Feb 24, 2010

    I have a table with log records that all specify a session start and end time for vpn users. I'd like to determine the max number of sessions active at once for a 24 hour period from 12:00:00 am to 11:59:59 pm. Is this code suitable for such a task?

  • Rami
    3 years ago
    Nov 29, 2009

    Ben,
    Is this logic is correct??? This is my first reply to your blog....
    select APP,MAX(cnt) as NoOfSessions from
    ( select s1.app,s.stime,COUNT(*) as cnt from (select app,starttime as sTime from sessions union select app,endtime from sessions) S
    inner join Sessions S1 on s.stime >= s1.starttime and s.sTime < s1.EndTime and s.app = s1.app
    group by s1.app,s.sTime )t group by app

    [Reply: Hi ddRamiReddy, and thanks for your first reply. :)
    This logic seems correct. I described a similar solution in the first part of the series (see http://sqlmag.com/Article/ArticleID/102734/sql_server_102734.html), and explained that it has quadratic complexity, and therefore is very slow.

    Cheers,
    Itzik]

You must log on before posting a comment.

Are you a new visitor? Register Here
  • SP1?
    I know there is a SP1 for SQL 2008 R2 available....and there is a "feature pack" as well... ...
  • SQL database mirroring
    I have SQL Server 2008 R2 Enterprise 64bit on Windows 2008 R2 Enterprise 64bit.  Each SQL Server has...
  • Dell Compellent Disk Drive
    Does anybody has experience with Dell Compellent Disk Drive? Basically, this system manages all disk...
  • Sql server performance tuning
    I need to find a tool that help me to optimize sql server,queries,improve the performance and solve ...