Count number of 'overlapping' rows in SQL Server - sql-server

I've been asked to look at a database that records user login and logout activity - there's a column for login time and then another column to record logout, both in OLE format. I need to pull together some information about user concurrency - i.e. how many users were logged in at the same time each day.
Do anyone know how to do this in SQL? I don't really need to know the detail, just the count per day.
Thanks in advance.

Easiest way is to make a times_table from an auxiliary numbers table (by adding from 0 to 24 * 60 minutes to the base time) to get every time in a certain 24-hour period:
SELECT MAX(simul) FROM (
SELECT test_time
,COUNT(*) AS simul
FROM your_login_table
INNER JOIN times_table -- a table/view/subquery of all times during the day
ON your_login_table.login_time <= times_table.test_time AND times_table.test_time <= your_login_table.logout_time
GROUP BY test_time
) AS simul_users (test_time, simul)

I think this will work.
Select C.Day, Max(C.Concurrency) as MostConcurrentUsersByDay
FROM
(
SELECT convert(varchar(10),L1.StartTime,101) as day, count(*) as Concurrency
FROM login_table L1
INNER JOIN login_table L2
ON (L2.StartTime>=L1.StartTime AND L2.StartTime<=L1.EndTime) OR
(L2.EndTime>=L1.StartTime AND L2.EndTime<=L1.EndTime)
WHERE (L1.EndTime is not null) and L2.EndTime Is not null) AND (L1.ID<>L2.ID)
GROUP BY convert(varchar(10),L1.StartTime,101)
) as C
Group BY C.Day

Unchecked... but lose date values, count time between, use "end of day" for still logged in.
This assumes "logintime" is a date and a time. If not, the derived table can be removed (Still need ISNULL though). of course, SQL Server 2008 has "time" to make this easier too.
SELECT
COUNT(*)
FROM
(
SELECT
DATEADD(day, DATEDIFF(day, logintime, 0), logintime) AS inTimeOnly,
ISNULL(DATEADD(day, DATEDIFF(day, logouttime, 0), logintime), '1900-01-01 23:59:59.997') AS outTimeOnly
FROM
mytable
) foo
WHERE
inTimeOnly >= #TheTimeOnly AND outTimeOnly <= #TheTimeOnly

Related

Most efficient way to get booking data for time period?

I have a booking system that allows a user to book places for 30 min timeslots (e.g. 1pm, 1:30pm, 2pm etc...)
In the sql database I may have one booking for 10am, a booking for 1pm and two for 2pm. I am trying to display a view of all 30 min booking slots in between a date time range displaying number of current bookings for each slot.
I am not storing each slot explicitly as it's not very efficient. Is there a way to make sql return 'empty' timeslots in a single query? I don't want to create a timeslot array then query each timeslot individually for the total count of bookings.
I am using sql server and asp.net mvc6 as my technology base. Some suggestions on technique would be appreciated.
Thanks.
you need to build a 30 minute interval time range table and do left join with your table to get all time slots
This query generates 30 minute interval times starting from startDate , total 12 time slots are generated, you can modify it accordingly.
declare #startDate datetime ='2014-01-12 12:00:00'
;with cte(value,nextval,n)
as
(
select CONVERT(VARCHAR(5),#startDate,108) as value,
dateadd(minute, datediff(minute, 0, #startDate)+30, 0) as nextval, 1 as n
union all
select CONVERT(VARCHAR(5),cte.nextval,108) as value,
dateadd(minute, datediff(minute, 0, cte.nextval)+30, 0) as nextval, n+1
from cte
where n <=12
)
select * from cte
left join Table1
on cte.nextval = Table1.timeslotvalue

Need help comparing difference between datetime stamps in SQL DB

I have a MSSQL server 2012 express DB that logs user activities. I need some help creating a query to compare timestamps on the user activities based on the text in the notes. I am interested in figuring out how long it takes my users to perform certain activities. The activities they are performing are stored in text in a NOTES column. I want to build a query that will tell me the time difference for each [INVOICEID] from the ‘START NOTE’ to the next note for that invoice by that user. The note that is entered is always the same for the start of the timer (for the purposes of this I used ‘START NOTE’ to indicate the start of the timer, but I have multiple activites I will need to do this for so I plan on simply changing that text in the query), but the end of the timer the text of the note will vary because it will be user entered data. I want to find the time difference between ‘START NOTE’ and the note that immediately follows ‘START NOTE’ entered by the same USERID for the same INVOICEID. Please see the SQLfiddle for an example of my data:
http://sqlfiddle.com/#!3/a00d7/1
With the data in the sql fiddle I would want the results of this query to be:
INVOICE ID USERID TIME_Difference
100 5 1 day
101 5 3 days
102 5 9 days
(time_difference does not need to be formatted like that, standard SQL formatting is fine)
I don’t really know where to start with this. Please let me know if you can help.
Thanks
select a.userid,a.invoiceid,min(a.added),min(b.added),datediff(DAY,min(a.added),min(b.added)) from om_note a
left join om_note b on a.userid=b.userid and a.invoiceid = b.invoiceid and a.added < b.added
where a.notes = 'START NOTE' group by a.userid,a.invoiceid
;with x as (
select
o.*, sum(case when notes='START NOTE' then 1 else 0 end)
over(partition by o.invoiceid, o.userid order by o.added) as grp
from om_note o
),
y as (
select *,
row_number() over(partition by x.invoiceid, x.userid, x.grp order by x.added) as rn
from x
where grp > 0
)
select y1.invoiceid, y1.userid, datediff(hour, y1.added, y2.added)
from y y1
inner join y y2
on y1.invoiceid=y2.invoiceid and y1.userid=y2.userid and y1.grp=y2.grp
where y1.rn=1 and y2.rn=2

How to get new clients by months in SQL Server

I have a table Clients.
Every client has da_reg (date registered in our system). I need to make a report:
By months - total number of clients (count(distinct customernumber)); and new customers by da_reg date (I can do this per month like insert all clients from past month into temp table and then compare WHERE da_reg < 'date' and customerid not in (select customerid from #temp) - however it takes a lot of time to every time compare).
How to make it easiest way? In 1-2 steps?
Please help!
Thanks in advance!
please try this
select count(*) as new_count,
month(da_reg) as month,year(da_reg) as year
(select count(*) from tbl a where tbl.da_reg>=a.da_reg) as total_cus
from tbl
group by month(da_reg),year(da_reg)
You can Select DAtE_TIME column by month and then calc an number of row :Example for August
SELECT *,count(a.id)
FROM TABLE A as a
WHERE DATEPART(month, MY_DATETIME) = 8
where a.id PK of A

Querying a running-percentage over a date range from MSSQL?

I want to graph the % of users over time that have their Twitter account connected. The number of users changes constantly, and so does the % of them that connect their Twitter account.
The table has a user account specific createDateTime column as well as a tw_connectDateTime column.
Let's say I'm interested in the trend of % connected over the last 7 days. Is there a way I can have MSSQL calculate the percentage for every day in the specified range, or do I need to do it myself using multiple queries?
Doing it in app logic would look something like (pseudocode):
for day in days:
query:
select
count(userId) as totalUsers
,c.connected
,cast(c.connected as float)/count(userId) as percentage
from
Users
outer apply (
select
count(userId) as connected
from
Users
where
tw_connectDateTime <= $day
) as c
where
createDateTime <= $day
group by
c.connected
What I'm unsure of is how, if it's possible, to expand this to run for each day, so that the results include the date as a column and the same values that I would get from running the above query for each date in the range.
Is it possible? If so, how?
actually you can use your query joined with days, like this:
with cte_days as (
select #DateStart as day
union all
select dateadd(dd, 1, c.[day]) as [day]
from cte_days as c
where c.[day] < #DateEnd
)
select
d.[day],
count(u.userId) as totalUsers,
c.connected,
cast(c.connected as float)/count(u.userId) as percentage
from cte_days as d
inner join Users as u on u.createDateTime <= d.[day]
outer apply (
select
count(T.userId) as connected
from Users as T
where T.tw_connectDateTime <= d.[day]
) as c
group by d.[day], c.connected

Transform server status data to running 24 hour tabular format

I need to display running server status, in an asp.net grid view using c#, for the last 24 hours in 5 minute increments. The data is in SQL Server records of the form: HostName, RecordDate, RecordTime, Status. I need to transform the data into tabular format to load a grid view control. Transform to something like: HostName, Date, 00:00 status, 00:05 status, ..., 23:55 status. One of the problems, of course, is the user can access the web page at any time. The column names must be the 5 minute increment time, as 15.30, 15.35, etc. They'll always be the same, as 24 hours will be displayed, but will be in a different order, and potentially cross dates, depending upon when the user logs into the web site. I hope I've explained this well enough. All options are on the table: linq, linq to sql, linq to xml, etc.
Thanks for any help.
I will offer a T-SQL solution. You need a date table that holds the 5-minute intervals for the day in question. Left join that with your AccessLog (or whatever it's called) table where the access time is within each time range, and do whatever aggregations you want. This will give you the vertical list. Then you need to PIVOT that to make your TimeRanges into columns (search for SQL server PIVOT operator).
Below is the rough SQL. After that, you just need to wrap the results into a pivot.
declare #myDate SMALLDATETIME = '20130415';
;with TimeRanges as (
SELECT TOP 288 DateAdd(minute, (Row_Number() over (order by sc1.Name) -1) * 5 , #myDate) TimeRangeMin
, DateAdd(minute, Row_Number() over (order by sc1.Name) * 5 , #myDate) TimeRangeMax
FROM Master.dbo.SysColumns sc1, Master.dbo.SysColumns sc2
)
select convert(varchar(5), TimeRangeMin, 114) AS TimeRange, COUNT(*)
from TimeRanges t
LEFT JOIN AccessLog a on a.AccessTime >= t.TimeRangeMin and a.AccessTime < t.TimeRangeMax
GROUP BY convert(varchar(5), TimeRangeMin, 114);

Resources