sql - max from group flag setter - sql

I m trying to achieve flag setting for the condition in my table below
p_id mon_year e_id flag
---- --------- ----- -----
1 2011/11 20 0
1 2011/11 21 1
1 2012/01 22 1
1 2012/02 23 0
1 2012/02 24 0
1 2012/02 25 1
2 2011/11 28 0
2 2011/11 29 1
2 2012/01 30 1
grouping by p_id,e_id and mon_year, the flag is set for the last value in the month.
I m confused how can i achieve this
I tried to achieved this by using row_number and partition to seperate out the value. By still looking for to achieved
Output by using row_number query , i have got is as below:
Grouping by
p_id mon_year e_id row
---- --------- ----- -----
1 2011/11 20 1
1 2011/11 21 2
1 2012/01 22 1
1 2012/02 23 1
1 2012/02 24 2
1 2012/02 25 3
2 2011/11 28 1
2 2011/11 29 2
2 2012/01 30 1
Max of this value would set the flag column. But i m really bugged how to achieve it. Any help would be useful.
Thanks !!

I think this is what you're going for. . . The output exactly matches your example:
declare #t table (p_id int, [year] int, [month] int, [day] int)
insert #t select 1, 2011, 11, 20
union select 1, 2011, 11, 21
union select 1, 2012, 01, 22
union select 1, 2012, 02, 23
union select 1, 2012, 02, 24
union select 1, 2012, 02, 25
union select 2, 2011, 11, 28
union select 2, 2011, 11, 29
union select 2, 2012, 01, 30
select p_id, [year], [month], [day]
, case when r=1 then 1 else 0 end flag
from
(
select p_id, [year], [month], [day]
, row_number() over (partition by p_id, [year], [month] order by [day] desc) r
from #t
) x
order by p_id, [year], [month], [day]
Output:
p_id year month day flag
1 2011 11 20 0
1 2011 11 21 1
1 2012 1 22 1
1 2012 2 23 0
1 2012 2 24 0
1 2012 2 25 1
2 2011 11 28 0
2 2011 11 29 1
2 2012 1 30 1

Try ordering by descending. In that way, you don't have to look for maximum ROW_NUMBER but when ROW_NUMBER is 1 ;)
Something like this (I didn't completely understand what you want to achieve, so this is probably not 100% accurate):
WITH r_MyTable
AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY mon_year ORDER BY p_id, e_id DESC) AS GroupRank
FROM MyTable
)
UPDATE r_MyTable
SET flag = CASE WHEN GroupRank = 1 THEN 1 ELSE 0 END;

You can use max statement on e_id to get last value for the month, code is below:
IF OBJECT_ID('tempdb..#tmptest') IS NOT NULL
DROP TABLE #tmptest
SELECT
*
INTO
#tmptest
FROM
(
SELECT '1' p_id, '2011/11' mon_year, '20' e_id, '0' flag UNION ALL
SELECT '1', '2011/11', '21', '1' UNION ALL
SELECT '1', '2012/01', '22', '1' UNION ALL
SELECT '1', '2012/02', '23', '0' UNION ALL
SELECT '1', '2012/02', '24', '0' UNION ALL
SELECT '1', '2012/02', '25', '1' UNION ALL
SELECT '2', '2011/11', '28', '0' UNION ALL
SELECT '2', '2011/11', '29', '1' UNION ALL
SELECT '2', '2012/01', '30', '1'
) as tmp
SELECT
tmptest.*
FROM
(
SELECT
MAX(e_id) e_id
,p_id
,mon_year
FROM
#tmptest
GROUP BY
p_id,mon_year
) tblLastValueEID
INNER JOIN
#tmptest tmptest
ON
tmptest.p_id = tblLastValueEID.p_id
AND
tmptest.mon_year = tblLastValueEID.mon_year
AND
tmptest.e_id = tblLastValueEID.e_id

Related

BigQuery - Picking latest not null value within 28 interval

I'm trying to add a column on this table and stuck for a little while
ID
Category 1
Date
Data1
A
1
2022-05-30
21
B
2
2022-05-21
15
A
2
2022-05-02
33
A
1
2022-02-11
3
B
2
2022-05-01
19
A
1
2022-05-15
null
A
1
2022-05-20
11
A
2
2022-04-20
22
to
ID
Category 1
Date
Data1
Picked_Data
A
1
2022-05-30
21
11
B
2
2022-05-21
15
19
A
2
2022-05-02
33
22
A
1
2022-02-11
3
some number or null
B
2
2022-05-01
19
some number or null
A
1
2022-05-15
null
some number or null
A
1
2022-05-20
11
some number or null
A
2
2022-04-20
22
some number or null
The logic is to partition by Category1 and ID then pick the latest none null value within the past 28 days. If there is no data exist, it'll be null
For the first row, ID = A and Category 1, it will pick 7th row as they are in the same category, ID and the date difference is <= 28. It skipped row 4th and 6th as the date is too far back and null value.
I've tried querying this by
select first_value(Data1) over (partition bty Category1 order by case when Data1 is not null and Date between Date - Inteverval 28 DAY and Date then 1 else 2) as Picked_Data
but it's picking incorrect rows,my guess is this query
Date between Date - Inteverval 28 DAY and Date
is not picking the correct date.. could anyone give me advise/suggestion how I could twick this query?
Consider below approach
select *,
first_value(data1 ignore nulls) over past_28_days as picked_data
from your_table
window past_28_days as (
partition by id, category_1
order by unix_date(date)
range between 29 preceding and 1 preceding
)
if applied to sample data in your question - output is
Consider below approach:
with sample_data as (
select 'A' as ID, 1 as category_1, date('2022-05-30') as date, 21 as data1,
union all select 'B' as ID, 2 as category_1, date('2022-05-21') as date, 15 as data1,
union all select 'A' as ID, 2 as category_1, date('2022-05-02') as date, 33 as data1,
union all select 'A' as ID, 1 as category_1, date('2022-02-11') as date, 3 as data1,
union all select 'B' as ID, 2 as category_1, date('2022-05-01') as date, 19 as data1,
union all select 'A' as ID, 1 as category_1, date('2022-05-15') as date, NULL as data1,
union all select 'A' as ID, 1 as category_1, date('2022-05-20') as date, 11 as data1,
union all select 'A' as ID, 2 as category_1, date('2022-04-20') as date, 22 as data1,
),
with_next_data as (
select *,
lag(date) over (partition by ID,category_1 order by date) as next_date,
lag(data1) over (partition by ID,category_1 order by date) as next_data,
from sample_data
)
select
id,
category_1,
date,
data1,
if(date_diff(date, next_date,day) <= 28, next_data, null) as picked_data
from with_next_data
Output:

Counts and divide from two different selects with dates

I have a table with this kind of structure (Sample only)
ID | STATUS | DATE |
--- -------- ------
1 OPEN 31-01-2022
2 CLOSE 15-11-2021
3 CLOSE 21-10-2021
4 OPEN 11-10-2021
5 OPEN 28-09-2021
I would like to know the counts of close vs open records by week. So it will be count(close)/count(open) where close.week = open.week
If there are no matching values, need to return 0 of course.
I got to this query below
SELECT *
FROM
(SELECT COUNT(*) AS 'CLOSE', DATEPART(WEEK, DATE) AS 'WEEKSA', DATEPART(YEAR, DATE) AS 'YEARA' FROM TABLE
WHERE STATUS IN ('CLOSE')
GROUP BY DATEPART(WEEK, DATE),DATEPART(YEAR, DATE)) TMPA
FULL OUTER JOIN
(SELECT COUNT(*) AS 'OPEN', DATEPART(WEEK, DATE) AS 'WEEKSB', DATEPART(YEAR, DATE) AS 'YEARB' FROM TABLE
WHERE STATUS IN ('OPEN')
GROUP BY DATEPART(WEEK, DATE),DATEPART(YEAR, DATE)) TMPB
ON TMPA.WEEKSA = TMPB.WEEKSB AND TMPA.YEARA = TMPB.YEARB
My results are as below (sample only)
close | weeksa | yeara | open | weeksb | yearb |
------ -------- ------ ------- ------- ------
3 2 2021
1 3 2021
1 4 2021
2 20 2021 2 20 2021
7 22 2021
2 23 2021
7 26 2021
7 27 2021
2 28 2021 14 28 2021
2 29 2021
10 30
24 31 2021
2 32 2021 5 32
4 33 2021
1 34 2021 13 34 2021
6 35 2021
1 36 2021
1 38 2021
1 39 2021
2 41 2021
4 43 2021
1 45 2021
2 46 2021 25 46 2021
1 47 2021 5 47 2021
4 48 2021
1 49 2021 20 49 2021
1 50 2021 17 50 2021
1 51 2021
How do I do the math now?
If I do another select the query fails. So I guess either syntax is bad or the whole concept is wrong.
The required result should look like this (Sample)
WEEK | YEAR | RATIO |
----- ------ -------
2 2021 0
3 2021 0
4 2021 0
5 2021 0.93
20 2021 0.1
22 2021 0
23 2021 0
26 2021 0
1 2022 0.75
2 2022 0.23
4 2022 0.07
Cheers!
I have added some test data to check the logic, adding the same in the code.
;with cte as(
select 1 ID, 'OPEN' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 10 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 11 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 12 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 22 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 32 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 2,'CLOSE',cast('2021-11-28' as DATE)
union select 3,'CLOSE',cast('2021-10-21' as DATE)
union select 8,'CLOSE',cast('2021-10-21' as DATE)
union select 9,'CLOSE',cast('2021-10-21' as DATE)
union select 4,'OPEN', cast('2021-10-11' as DATE)
union select 5,'CLOSE', cast('2021-09-28' as DATE)
union select 6,'OPEN', cast('2021-09-27' as DATE)
union select 7,'CLOSE', cast('2021-09-26' as DATE) )
, cte2 as (
select DATEPART(WEEK,date) as week_number,* from cte)
,cte3 as(
select week_number,year(date) yr,count(case when status = 'open' then 1 end)open_count,count(case when status <> 'open' then 1 end) close_count from cte2 group by week_number,year(date))
select week_number as week,yr as year,
cast(case when open_count = 0 then 1.0 else open_count end /
case when close_count = 0 then 1.0 else close_count end as numeric(3,2)) as ratio
from cte3

I want cummulative row for a given input

I have table like below
Months cnt
Jan 2
Feb 3
Mar 5
I want output like below
Months cnt
Jan 2
Feb 2
Feb 3
Mar 2
Mar 3
Mar 5
I tried using below query but not getting the required output
Select distinct months, cnt, level
from (select months, cnt, rownum row_cnt
from tablename)
connect by level <= row_cnt
Order by months, cnt, level
Here's one option which converts month's names into their ordinal number (1 for Jan, 2 for Feb, etc.) and then - using self join - returns the result.
SQL> with test (months, cnt) as
2 (select 'jan', 2 from dual union all
3 select 'feb', 3 from dual union all
4 select 'mar', 5 from dual
5 ),
6 temp as
7 (select
8 months,
9 to_number(to_char(to_date(months, 'mon', 'nls_date_language=english'), 'mm')) mon,
10 cnt
11 from test
12 )
13 select a.months, b.cnt
14 from temp a join temp b on a.mon >= b.mon
15 order by a.mon, b.cnt;
MON CNT
--- ----------
jan 2
feb 2
feb 3
mar 2
mar 3
mar 5
6 rows selected.
SQL>
You need a self join:
select t.months, tt.cnt
from tablename t inner join tablename tt
on extract(month from to_date(t.Months,'MON')) >= extract(month from to_date(tt.Months,'MON'))
order by extract(month from to_date(t.Months,'MON')), tt.cnt
See the demo.
Results:
> MONTHS | CNT
> :----- | --:
> Jan | 2
> Feb | 2
> Feb | 3
> Mar | 2
> Mar | 3
> Mar | 5

Date in SQL Pivot to be transformed from yyyy-mm-dd to mm-yy

I have a pivot query IN SQL Server 2016 that works somewhat good:
select *
FROM (select Analyst,Task_Type,Load,Completed,Date_Of_Assignment
from mytable where Task_Type IN ('Loans') ) as TEST PIVOT
(
SUM(Completed)
FOR
[Date_Of_Assignment] IN ([2018-05-22],[2018-06-22],[2018-07-22])
) as P
it returns as:
Agent_Name Task_Type Total_Received 2018-05-22 2018-06-22 2018-07-22
Steve Loans 20 15 15 15
Cathe Loans 20 15 15 15
Adam Loans 20 15 15 15
Klaus Loans 20 15 15 15
which is cool, but I need it more like this
Agent_Name Task_Type Total_Received May 18 June 18 July 18
Steve Loans 20 15 15 15
Cathe Loans 20 15 15 15
Adam Loans 20 15 15 15
Klaus Loans 20 15 15 15
Considering that it might be more than one day of data per Load, actually it should be every workday.
I tried by grouping by Date_ but it returns errors.
If I do like:
select *
FROM (select Analyst,Task_Type,Load,Completed,Date_Of_Assigment
CASE WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '05-18' THEN 'May 18'
WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '06-18' THEN 'Jun 18'
WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '07-18' THEN 'Jul 18'
END AS MonthT
from mytable where Task_Type IN ('Loans') as TEST PIVOT
(
SUM(Completed)
FOR
[MonthT] IN (['May 18'],['Jun 18'],['Jul 18'])
) as P
I receive:
Analyst Task Total_Received 'May 18' 'Jun 18' 'Jul 18'
Steve Billing 20 NULL NULL NULL
Cathe Billing 20 NULL NULL NULL
Adam Billing 20 NULL NULL NULL
Klaus Billing 20 NULL NULL NULL
It does turn Dates into columns but with NULLS only.
Data types are: Date for Date_Of_Assigment, nvarchar for Task_Type and Analyst and integer for the rest of columns.
What am I doing wrong?
Thank you very much
I would do this as :
select Analyst, Task,
sum(case when MonthT = '05-18' then Completed else 0 end) [May 18],
sum(case when MonthT = '06 18' then Completed else 0 end) [Jun 18],
sum(case when MonthT = '05-18' then Completed else 0 end) [Jul 18]
from table t cross apply
( values format (Date_Of_Assigment, 'MM-yy')
) tt (MonthT)
where Task_Type = 'Loans'
group by Analyst, Task;
Try this:
SELECT
*
FROM (
SELECT
Analyst
, Task_Type
, [Load]
, Completed
, DATENAME( MM, Date_Of_Assignment ) + ' ' + CAST( ( YEAR( Date_Of_Assignment ) % 100 ) AS VARCHAR(2) ) AS Date_Of_Assignment
FROM mytable WHERE Task_Type IN ( 'Loans' )
) AS TEST
PIVOT (
SUM(Completed)
FOR
[Date_Of_Assignment] IN ( [May 18], [June 18], [July 18] )
) AS P
I don't have access to your data to test this, but in theory, it should do what you're looking for.
Here's a simple proof of concept you can run in SSMS:
DECLARE #mytable TABLE (
Analyst VARCHAR(10), Task_Type VARCHAR(10) DEFAULT 'Loans', [Load] INT, Completed INT, Date_Of_Assignment DATETIME
)
INSERT INTO #mytable ( Analyst, [Load], Completed, Date_Of_Assignment )
VALUES
( 'Steve', 3, 2, '05/22/2018' )
, ( 'Steve', 1, 1, '06/22/2018' )
, ( 'Steve', 4, 3, '07/22/2018' );
SELECT
Analyst, Task_Type, SUM( [Load] ) AS Total_Recieved, SUM( [May 18] ) AS [May 18], SUM( [June 18] ) AS [June 18], SUM( [July 18] ) AS [July 18]
FROM (
SELECT
Analyst
, Task_Type
, [Load]
, Completed
, DATENAME( MM, Date_Of_Assignment ) + ' ' + CAST( ( YEAR( Date_Of_Assignment ) % 100 ) AS VARCHAR(2) ) AS Date_Of_Assignment
FROM #mytable
WHERE Task_Type IN ( 'Loans' )
) AS TEST
PIVOT (
SUM( Completed ) FOR [Date_Of_Assignment] IN ( [May 18], [June 18], [July 18] )
) AS P
GROUP BY Analyst, Task_Type;
Returns
+---------+-----------+----------------+--------+---------+---------+
| Analyst | Task_Type | Total_Recieved | May 18 | June 18 | July 18 |
+---------+-----------+----------------+--------+---------+---------+
| Steve | Loans | 8 | 2 | 1 | 3 |
+---------+-----------+----------------+--------+---------+---------+

SQL max concurrent sessions per hour of day

I work in a healthcare call-centre environment. I have access to our core system's database and I want to use the data in it to determine the number of staff logged on each hour of the data in a given period that may expand over 24 hours (e.g. 24/12/2014 - 26/12/2014).
There is a userlog table that records when users log on and log off.
userlog table example
UserRef Date LogType SessionID
--------------------------------------- ----------------------- ------ -----------------------
96AD647C-D061-43F5-9F8D-FA6C74817E07 2002-10-17 14:11:25.763 LOGON 8D451569-0260-46BB-9B9E-F49B3E778161
96AD647C-D061-43F5-9F8D-FA6C74817E07 2002-10-17 18:11:32.547 LOGOFF 8D451569-0260-46BB-9B9E-F49B3E778161
60738820-5F72-4E20-A070-57E07C83B6DE 2002-10-17 14:53:31.153 LOGON C773894C-8B2D-4054-A550-3F04B4C5669F
60738820-5F72-4E20-A070-57E07C83B6DE 2002-10-17 22:55:25.607 LOGOFF C773894C-8B2D-4054-A550-3F04B4C5669F
90A55FDD-967E-4D99-96DF-96840CDB2CDF 2002-10-17 15:26:40.123 LOGON 1CE5F5A5-4E20-4D4A-BB67-EB0CB33976D7
96AD647C-D061-43F5-9F8D-FA6C74817E07 2002-10-17 15:51:28.590 LOGON 7EFDEE1C-15CF-4DE1-B59F-7AFC49B4BE73
90A55FDD-967E-4D99-96DF-96840CDB2CDF 2002-10-17 15:58:05.217 LOGOFF 1CE5F5A5-4E20-4D4A-BB67-EB0CB33976D7
96AD647C-D061-43F5-9F8D-FA6C74817E07 2002-10-17 15:58:31.013 LOGOFF 7EFDEE1C-15CF-4DE1-B59F-7AFC49B4BE73
90A55FDD-967E-4D99-96DF-96840CDB2CDF 2002-10-17 15:58:32.733 LOGON 03F56AB8-FED5-4CC7-8445-26BF55F58E60
90A55FDD-967E-4D99-96DF-96840CDB2CDF 2002-10-17 16:13:02.827 LOGOFF 03F56AB8-FED5-4CC7-8445-26BF55F58E60
Desired results (this is not intended to reflect the above sample data):
Date Hour Number of users logged in
---- ---- -------------------------
01/12/2014 0 0
01/12/2014 1 0
01/12/2014 2 0
01/12/2014 3 0
01/12/2014 4 0
01/12/2014 5 1
01/12/2014 6 1
01/12/2014 7 1
01/12/2014 8 3
01/12/2014 9 7
01/12/2014 10 7
...
01/12/2014 23 0
To be clear: I'm trying to get the hour to still display with a 0 count when there were no users logged in.
I guess what I'm looking for is the maximum concurrent sessions for each hour of the day, but I'm not overly technical or skilled in SQL (getting better bit by bit, though!) so I hope that terminology doesn't confuse things!
I've googled for this and found a few similar scenarios, but for Oracle and MySQL, or where the log table records the logon data differently. I'm sure I'll get to a point where I can successfully 'translate' other database query code to MS SQL, but I'm not there yet!
I am using: Microsoft SQL Server 2005 - 9.00.1399.06 (Intel X86).
Thanks in advance for your help.
UPDATED (v # 4):
I've added date filter at the end of the query - WHERE A.[Date] Between #X and #Y - it's not most efficient way in this case but simplest I think and less error prone for a start :
SELECT
A.[Date],
A.[Hour],
SUM(CASE WHEN (B.[SessionID] IS NULL) THEN 0 ELSE 1 END) AS [Number_of_Sessions_Per_Hour]
FROM
(
SELECT DISTINCT
CONVERT(DATETIME,
LTRIM(RTRIM(CONVERT(NVARCHAR(10), YEAR(userlog.[Date]))))
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), MONTH(userlog.[Date])))), 2)
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), DAY(userlog.[Date])))), 2)
, 120) AS [Date],
hours_table.[Hour]
FROM
userlog,
(
SELECT 1 AS [Hour] UNION ALL SELECT 3 AS [Hour] UNION ALL SELECT 4 AS [Hour] UNION ALL SELECT 5 AS [Hour] UNION ALL SELECT 6 AS [Hour]
UNION ALL SELECT 7 AS [Hour] UNION ALL SELECT 7 AS [Hour] UNION ALL SELECT 8 AS [Hour] UNION ALL SELECT 9 AS [Hour] UNION ALL SELECT 10 AS [Hour]
UNION ALL SELECT 11 AS [Hour] UNION ALL SELECT 12 AS [Hour] UNION ALL SELECT 13 AS [Hour] UNION ALL SELECT 14 AS [Hour] UNION ALL SELECT 15 AS [Hour]
UNION ALL SELECT 16 AS [Hour] UNION ALL SELECT 17 AS [Hour] UNION ALL SELECT 18 AS [Hour] UNION ALL SELECT 19 AS [Hour] UNION ALL SELECT 20 AS [Hour]
UNION ALL SELECT 21 AS [Hour] UNION ALL SELECT 22 AS [Hour] UNION ALL SELECT 23 AS [Hour] UNION ALL SELECT 24 AS [Hour]
) as hours_table
) AS A
LEFT OUTER JOIN
(
SELECT
userlog.SessionID,
MAX(CASE WHEN userlog.LogType = 'LOGON'
THEN CONVERT(DATETIME,
LTRIM(RTRIM(CONVERT(NVARCHAR(10), YEAR(userlog.[Date]))))
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), MONTH(userlog.[Date])))), 2)
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), DAY(userlog.[Date])))), 2)
, 120)
ELSE CONVERT(DATETIME, '1900-01-01', 120)
END) AS [Date_Session_START],
MAX(CASE WHEN userlog.LogType = 'LOGOFF'
THEN CONVERT(DATETIME,
LTRIM(RTRIM(CONVERT(NVARCHAR(10), YEAR(userlog.[Date]))))
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), MONTH(userlog.[Date])))), 2)
+ '-' + RIGHT('0' + LTRIM(RTRIM(CONVERT(NVARCHAR(10), DAY(userlog.[Date])))), 2)
, 120)
ELSE CONVERT(DATETIME, '1900-01-01', 120)
END) AS [Date_Session_END],
MAX(CASE WHEN userlog.LogType = 'LOGON' THEN DATEPART(HOUR, userlog.[Date]) ELSE 0 END) AS [Hour_Session_START],
MAX(CASE WHEN userlog.LogType = 'LOGOFF' THEN DATEPART(HOUR, userlog.[Date]) ELSE 0 END) AS [Hour_Session_END],
FROM
userlog
GROUP BY
userlog.SessionID
) AS B
ON (A.[Date] >= B.[Date_Session_START] AND A.[Date] <= B.[Date_Session_END])
AND (A.[Hour] >= B.[Hour_Session_START] AND A.[Hour] <= B.[Hour_Session_END])
WHERE
A.[Date] Between #X and #Y
GROUP BY
A.[Date],
A.[Hour]
OK, taken a slightly different approach. I build a table of Logon / Log Off - which could be in a cte but Im not an oracle expert so not sure if its supported so gone for simpler SQL and a temp table. I have all built a table of static integers (also known as tally table) for 0-30 to handle a test date range in my query based on your answer above - if you have one already you could just use that ranged down and save some effort. Running the query returns a table like this (using your sample data also below)
StartDate WorkingHour LoggedInUsers
2002-10-17 00:00:00.000 10 0
2002-10-17 00:00:00.000 11 0
2002-10-17 00:00:00.000 12 0
2002-10-17 00:00:00.000 13 0
2002-10-17 00:00:00.000 14 2
2002-10-17 00:00:00.000 15 5
2002-10-17 00:00:00.000 16 3
2002-10-17 00:00:00.000 17 2
2002-10-17 00:00:00.000 18 2
2002-10-17 00:00:00.000 19 1
2002-10-17 00:00:00.000 20 1
2002-10-17 00:00:00.000 21 1
2002-10-17 00:00:00.000 22 1
2002-10-17 00:00:00.000 23 0
Code as Run in sql
DECLARE #StartDate DATETIME ,
#NoDays INT ;
select
#StartDate = '2002-10-01',
#NoDays = 20;
DECLARE #Sessions TABLE (
UserRef UNIQUEIDENTIFIER,
DATE DATETIME,
LogType VARCHAR(100),
SessionID UNIQUEIDENTIFIER
);
INSERT INTO #Sessions
SELECT '96AD647C-D061-43F5-9F8D-FA6C74817E07', '2002-10-17 14:11:25.763', 'LOGON', '8D451569-0260-46BB-9B9E-F49B3E778161'
UNION SELECT '96AD647C-D061-43F5-9F8D-FA6C74817E07', '2002-10-17 18:11:32.547', 'LOGOFF', '8D451569-0260-46BB-9B9E-F49B3E778161'
UNION SELECT '60738820-5F72-4E20-A070-57E07C83B6DE', '2002-10-17 14:53:31.153', 'LOGON', 'C773894C-8B2D-4054-A550-3F04B4C5669F'
UNION SELECT '60738820-5F72-4E20-A070-57E07C83B6DE', '2002-10-17 22:55:25.607', 'LOGOFF', 'C773894C-8B2D-4054-A550-3F04B4C5669F'
UNION SELECT '90A55FDD-967E-4D99-96DF-96840CDB2CDF', '2002-10-17 15:26:40.123', 'LOGON', '1CE5F5A5-4E20-4D4A-BB67-EB0CB33976D7'
UNION SELECT '96AD647C-D061-43F5-9F8D-FA6C74817E07', '2002-10-17 15:51:28.590', 'LOGON', '7EFDEE1C-15CF-4DE1-B59F-7AFC49B4BE73'
UNION SELECT '90A55FDD-967E-4D99-96DF-96840CDB2CDF', '2002-10-17 15:58:05.217', 'LOGOFF', '1CE5F5A5-4E20-4D4A-BB67-EB0CB33976D7'
UNION SELECT '96AD647C-D061-43F5-9F8D-FA6C74817E07', '2002-10-17 15:58:31.013', 'LOGOFF', '7EFDEE1C-15CF-4DE1-B59F-7AFC49B4BE73'
UNION SELECT '90A55FDD-967E-4D99-96DF-96840CDB2CDF', '2002-10-17 15:58:32.733', 'LOGON', '03F56AB8-FED5-4CC7-8445-26BF55F58E60'
UNION SELECT '90A55FDD-967E-4D99-96DF-96840CDB2CDF', '2002-10-17 16:13:02.827', 'LOGOFF', '03F56AB8-FED5-4CC7-8445-26BF55F58E60';
DECLARE #staticintegers TABLE (myInteger INT);
INSERT INTO #staticintegers
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13
UNION SELECT 14 UNION SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18
UNION SELECT 19 UNION SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23
UNION SELECT 24 UNION SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28
UNION SELECT 29 UNION SELECT 30 UNION SELECT 31 UNION SELECT 32 UNION SELECT 33;
DECLARE #sessionsOutput TABLE (SessionID UNIQUEIDENTIFIER, StartTime DATETIME, EndTime DATETIME);
INSERT INTO #sessionsOutput
(SessionID, StartTime)
SELECT
SessionID,
[date]
FROM
#Sessions
WHERE logtype = 'Logon';
UPDATE #sessionsOutput
SET EndTime = [date]
FROM #sessionsOutput aa
INNER JOIN #Sessions bb
ON aa.SessionID = bb.SessionID
WHERE bb.LogType = 'Logoff';
SELECT
DATEADD(dd, DateIntegers.myInteger, #StartDate) AS StartDate
,hoursintegers.myINteger AS WorkingHour
,COUNT(aa.SessionID) AS LoggedInUsers
FROM
#staticintegers DateIntegers
LEFT OUTER JOIN #StaticIntegers HoursIntegers
ON HoursIntegers.myInteger BETWEEN 0 AND 23
LEFT OUTER JOIN #sessionsOutput aa
ON
HoursIntegers.myInteger BETWEEN DATEPART(hh, aa.StartTime) AND DATEPART(hh, aa.endtime)
AND CAST(aa.StartTime AS DATE) = DATEADD(dd, dateintegers.myInteger, #StartDate)
GROUP BY
DATEADD(dd, DateIntegers.myInteger, #StartDate),
HoursIntegers.myInteger
ORDER BY
DATEADD(dd, DateIntegers.myInteger, #StartDate),
HoursIntegers.myInteger;