SSRS Report shows data for different dates - sql

My report(which calculates attendance %) shows aggregated data if I remove the sessiondate field.
But I need it as users need to run it each month(currently we run query manually)
So, I have sessiondate field in my query which is based on start and end date parameters
Query:
SELECT sessiondate,
Possible,
Present,
SUM(Possible_Duration) AS Possible_Duration,
SUM(Present_Duration) AS Present_Duration,
CASE WHEN SUM(Present_Duration) = 0 THEN 0
ELSE
SUM(Present_Duration) / SUM(Possible_Duration) END AS Attendance,
SUM(CASE WHEN Mark = 'L' THEN 1 ELSE 0 END) AS Late_Mark,
SUM(Possible) AS Possib,
SUM(CASE WHEN Mark = 'A' THEN 1 ELSE 0 END) AS Authorised_absence,
SUM(CASE WHEN Mark = '/' THEN 1 ELSE 0 END) AS Present_,
SUM(CASE WHEN Mark = 'O' THEN 1 ELSE 0 END) AS Absent_fromClass
FROM Table
WHERE Sessiondate >= #startdate
AND Sessiondate <= #Enddate
GROUP BY Session_dt
Query result:
--------------------------------------
col1 | col2 | Session_dt | attendance
--------------------------------------
A | B | 01/01/2015 | 100
A | B | 03/01/2015 | 69
A | B | 05/01/2015 | 100
Expected result:
--------------------------
col1 | col2 | attendance
--------------------------
A | B | 89.3
For now I just specified dates in my query and running the report. Is there any way I can achieve this using parameter values?

This query will return data between first and last date of the current month, then you only need to schedule your report to run every month end or something:
select *
where [Date] between
CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(GETDATE())-1),GETDATE()),101) --first date
and
CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,GETDATE()))),DATEADD(mm,1,GETDATE())),101) --last date

You can put one parameter on your SSRS report as #monthNo and simply display the month list of Jan - Dec with simply value of 1 -12.
On SQL side, you could receive month number that the user selected and use it like this:
declare #monthNo as char(2) = '1'; -- remove this from your SQL
declare #firstDate as datetime;
declare #lastDate as datetime;
select #firstDate = CONVERT(datetime,CONVERT(CHAR(4),year(getdate())) + '-' + #monthNo + '-1');
select #lastDate = dateadd(day,-1,(dateadd(month,1,#firstDate)));
select *
from [table]
where [date] between #firstDate and #lastDate;
Now you can run the report for whatever month.

Related

SSRS Expression "Count of Days with No dissatisfied Customers" Cannot get it working

I have to get a expression in my SSRS table that achieves the count of days with no dissatisfied customers.
Right now I have an expression like this:
=RunningValue(IIF(Fields!SATISFACTION_LEVEL.Value <> "Dissatisfied",1,0),Sum, "DataSet1")
This gives me the number of rows that contain a satisfaction level other than Dissatisfied.
My issue is that I can't seem to get a count of days where there was no dissatisfied customer. I can't find a solution to counting the days. Essentially this is what it should do. If there was a record that day with a dissatisfied customer, don't count it. If there was no dissatisfied customers, tally it.
This will need to be done for the current year to date, and also for the year before.
I would really appreciate any help with this expression!
Thanks
UPDATE MORE INFO:
dataset structure is like this:
_______________________________________
| satisfaction_level | Date |
---------------------------------------
| Satisfied | 07/20/2020 |
| dissatisfied | 07/20/2020 |
| Satisfied | 07/20/2020 |
| Highly Satisfied | 07/20/2020 |
| Satisfied | 07/20/2020 |
| Satisfied | 07/21/2020 |
| Satisfied | 07/21/2020 |
| Highly Satisfied | 07/21/2020 |
expected functionality - for the day of 7/20/2020 there was 1 dissatisfied customer (do not tally), for the day of 7/21/2020 there were NO dissatisfied customers (tally). Resulting in a total number of days where there were NO dissatisfied customers. I hope this helps further explain the outcome needed.
Put SSRS to the side for now, the problem with counting days of anything is that its hard to count a row that is not there. For instance if I have a number of response records spread out over a week, but they only fall on 4 of the days, when we group by day, the query can only return results for the days that existed in the recordset:
DECLARE #Responses as Table
(
ENTRY_TIME DateTimeOffset, SATISFACTION_LEVEL VARCHAR(20)
)
INSERT INTO #Responses
VALUES
('2020-01-4', 'Dissatisfied'),
('2020-01-4', 'Dissatisfied'),
('2020-01-1', 'Satisfied'),
('2020-01-5', 'Dissatisfied'),
('2020-01-5', 'Satisfied'),
('2020-01-2', 'Dissatisfied')
SELECT
fn.DATE
, DATENAME(WEEKDAY, fn.DATE) as [Day]
, SUM(CASE SATISFACTION_LEVEL WHEN 'Dissatisfied' THEN 1 ELSE 0 END) as [Dissatisfied]
FROM #Responses
CROSS APPLY (SELECT CAST(ENTRY_TIME as Date) as [DATE]) as fn
GROUP BY fn.DATE
ORDER BY fn.DATE
DATE Day Dissatisfied
---------- ------------------------------ ------------
2020-01-01 Wednesday 0
2020-01-02 Thursday 1
2020-01-04 Saturday 2
2020-01-05 Sunday 1
(4 rows affected)
We can solve this problem by generating a series record set that we can join our real world data against that will ensure that we have a row for each day.
This can be achieved through the use of a recursive CTE, in the query below the grouped data result is joined to the series data, you could do this any number of different ways, you could even pivot the SATISFACTION_LEVEL column responses, this is just to illustrate
the technique of pre-processing the data in SQL before formatting it in an SSRS report:
DECLARE #From Date = '2019-12-30';
DECLARE #To Date = '2020-01-05';
;
WITH [Sequence] ([Date])
as
(
SELECT #From
UNION ALL
SELECT DATEADD(DAY, 1, [Date]) FROM [Sequence]
WHERE [Date] < #To
)
, [GroupedByDay]
as
(
SELECT
fn.DATE
, SUM(CASE SATISFACTION_LEVEL WHEN 'Dissatisfied' THEN 1 ELSE 0 END) as [Dissatisfied]
, SUM(CASE SATISFACTION_LEVEL WHEN 'Satisfied' THEN 1 ELSE 0 END) as [Satisfied]
FROM #Responses
CROSS APPLY (SELECT CAST(ENTRY_TIME as Date) as [DATE]) as fn
GROUP BY fn.DATE
)
SELECT
c.[Date]
, DATENAME(WEEKDAY, c.[DATE]) as [Day]
, ISNull([Dissatisfied],0) as [Dissatisfied]
, ISNULL([Satisfied],0) as [Satisfied]
FROM [GroupedByDay] g
RIGHT OUTER JOIN [Sequence] c ON g.[DATE] = c.[Date]
ORDER BY c.[Date]
Date Day Dissatisfied Satisfied
---------- ------------------------------ ------------ -----------
2019-12-30 Monday 0 0
2019-12-31 Tuesday 0 0
2020-01-01 Wednesday 0 1
2020-01-02 Thursday 1 0
2020-01-03 Friday 0 0
2020-01-04 Saturday 2 0
2020-01-05 Sunday 1 1
(7 rows affected)
Without specific information about your schema and current query, that's about the best I can offer, however data by day should be more than enough for you group this into year on year results within SSRS...
Or you could do it directly in SQL too if you want :)
#Update: Example where just the total count of days where there are no dissatisfied customers is returned:
DECLARE #From Date = '2019-12-30';
DECLARE #To Date = '2020-01-05';
;
WITH [Sequence] ([Date])
as
(
SELECT #From
UNION ALL
SELECT DATEADD(DAY, 1, [Date]) FROM [Sequence]
WHERE [Date] < #To
)
, [GroupedByDay]
as
(
SELECT
fn.DATE
, SUM(CASE SATISFACTION_LEVEL WHEN 'Dissatisfied' THEN 1 ELSE 0 END) as [Dissatisfied]
, SUM(CASE SATISFACTION_LEVEL WHEN 'Satisfied' THEN 1 ELSE 0 END) as [Satisfied]
FROM #Responses
CROSS APPLY (SELECT CAST(ENTRY_TIME as Date) as [DATE]) as fn
GROUP BY fn.DATE
)
, [InjectedMissingDays]
as
(
SELECT
c.[Date]
, DATENAME(WEEKDAY, c.[DATE]) as [Day]
, ISNull([Dissatisfied],0) as [Dissatisfied]
, ISNULL([Satisfied],0) as [Satisfied]
FROM [GroupedByDay] g
RIGHT OUTER JOIN [Sequence] c ON g.[DATE] = c.[Date]
)
--Overall
SELECT COUNT(1) as [Days with No Dissatisfied Customers] FROM [InjectedMissingDays] WHERE Dissatisfied = 0
Days with No Dissatisfied Customers
-----------------------------------
4
(1 row affected)

SQL how to count census points occurring between date records

I’m using MS-SQL-2008 R2 trying to write a script that calculates the Number of Hospital Beds occupied on any given day, at 2 census points: midnight, and 09:00.
I’m working from a data set of patient Ward Stays. Basically, each row in the table is a record of an individual patient's stay on a single ward, and records the date/time the patient is admitted onto the ward, and the date/time the patient leaves the ward.
A sample of this table is below:
Ward_Stay_Primary_Key | Ward_Start_Date_Time | Ward_End_Date_Time
1 | 2017-09-03 15:04:00.000 | 2017-09-27 16:55:00.000
2 | 2017-09-04 18:08:00.000 | 2017-09-06 18:00:00.000
3 | 2017-09-04 13:00:00.000 | 2017-09-04 22:00:00.000
4 | 2017-09-04 20:54:00.000 | 2017-09-08 14:30:00.000
5 | 2017-09-04 20:52:00.000 | 2017-09-13 11:50:00.000
6 | 2017-09-05 13:32:00.000 | 2017-09-11 14:49:00.000
7 | 2017-09-05 13:17:00.000 | 2017-09-12 21:00:00.000
8 | 2017-09-05 23:11:00.000 | 2017-09-06 17:38:00.000
9 | 2017-09-05 11:35:00.000 | 2017-09-14 16:12:00.000
10 | 2017-09-05 14:05:00.000 | 2017-09-11 16:30:00.000
The key thing to note here is that a patient’s Ward Stay can span any length of time, from a few hours to many days.
The following code enables me to calculate the number of beds at both census points for any given day, by specifying the date in the case statement:
SELECT
'05/09/2017' [Date]
,SUM(case when Ward_Start_Date_Time <= '05/09/2017 00:00:00.000' AND (Ward_End_Date_Time >= '05/09/2017 00:00:00.000' OR Ward_End_Date_Time IS NULL)then 1 else 0 end)[No. Beds Occupied at 00:00]
,SUM(case when Ward_Start_Date_Time <= '05/09/2017 09:00:00.000' AND (Ward_End_Date_Time >= '05/09/2017 09:00:00.000' OR Ward_End_Date_Time IS NULL)then 1 else 0 end)[No. Beds Occupied at 09:00]
FROM
WardStaysTable
And, based on the sample 10 records above, generates this output:
Date | No. Beds Occupied at 00:00 | No. Beds Occupied at 09:00
05/09/2017 | 4 | 4
To perform this for any number of days is obviously onerous, so what I’m looking to create is a query where I can specify a start/end date parameter (e.g. 1st-5th Sept), and for the query to then evaluate the Ward_Start_Date_Time and Ward_End_Date_Time variables for each record, and – grouping by the dates defined in the date parameter – count each time the 00:00:00.000 and 09:00:00.000 census points fall between these 2 variables, to give an output something along these lines (based on the above 10 records):
Date | No. Beds Occupied at 00:00 | No. Beds Occupied at 09:00
01/09/2017 | 0 | 0
02/09/2017 | 0 | 0
03/09/2017 | 0 | 0
04/09/2017 | 1 | 1
05/09/2017 | 4 | 4
I’ve approached this (perhaps naively) thinking that if I use a cte to create a table of dates (defined by the input parameters), along with associated midnight and 9am census date/time points, then I could use these variables to group and evaluate the dataset.
So, this code generates the grouping dates and census date/time points:
DECLARE
#StartDate DATE = '01/09/2017'
,#EndDate DATE = '05/09/2017'
,#0900 INT = 540
SELECT
DATEADD(DAY, nbr - 1, #StartDate) [Date]
,CONVERT(DATETIME,(DATEADD(DAY, nbr - 1, #StartDate))) [MidnightDate]
,DATEADD(mi, #0900,(CONVERT(DATETIME,(DATEADD(DAY, nbr - 1, #StartDate))))) [0900Date]
FROM
(
SELECT
ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS nbr
FROM sys.columns c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
The stumbling block I’ve hit is how to join the cte to the WardStays dataset, because there’s no appropriate key… I’ve tried a few iterations of using a subquery to make this work, but either I’m taking the wrong approach or I’m getting my syntax in a mess.
In simple terms, the logic I’m trying to create to get the output is something like:
SELECT
[Date]
,SUM (case when WST.Ward_Start_Date_Time <= [MidnightDate] AND (WST.Ward_End_Date_Time >= [MidnightDate] OR WST.Ward_End_Date_Time IS NULL then 1 else 0 end) [No. Beds Occupied at 00:00]
,SUM (case when WST.Ward_Start_Date_Time <= [0900Date] AND (WST.Ward_End_Date_Time >= [0900Date] OR WST.Ward_End_Date_Time IS NULL then 1 else 0 end) [No. Beds Occupied at 09:00]
FROM WardStaysTable WST
GROUP BY [Date]
Is the above somehow possible, or am I barking up the wrong tree and need to take a different approach altogether? Appreciate any advice.
I would expect something like this:
WITH dates as (
SELECT CAST(#StartDate as DATETIME) as dte
UNION ALL
SELECT DATEADD(DAY, 1, dte)
FROM dates
WHERE dte < #EndDate
)
SELECT dates.dte [Date],
SUM(CASE WHEN Ward_Start_Date_Time <= dte AND
Ward_END_Date_Time >= dte
THEN 1 ELSE 0
END) as num_beds_0000,
SUM(CASE WHEN Ward_Start_Date_Time <= dte + CAST('09:00' as DATETIME) AND
Ward_END_Date_Time >= dte + CAST('09:00' as DATETIME)
THEN 1 ELSE 0
END) as num_beds_0900
FROM dates LEFT JOIN
WardStaysTable wt
ON wt.Ward_Start_Date_Time <= DATEADD(day, 1, dates.dte) AND
wt.Ward_END_Date_Time >= dates.dte
GROUP BY dates.dte
ORDER BY dates.dte;
The cte is just creating the list of dates.
What a cool exercise. Here is what I came up with:
CREATE TABLE #tmp (ID int, StartDte datetime, EndDte datetime)
INSERT INTO #tmp values(1,'2017-09-03 15:04:00.000','2017-09-27 06:55:00.000')
INSERT INTO #tmp values(2,'2017-09-04 08:08:00.000','2017-09-06 18:00:00.000')
INSERT INTO #tmp values(3,'2017-09-04 13:00:00.000','2017-09-04 22:00:00.000')
INSERT INTO #tmp values(4,'2017-09-04 20:54:00.000','2017-09-08 14:30:00.000')
INSERT INTO #tmp values(5,'2017-09-04 20:52:00.000','2017-09-13 11:50:00.000')
INSERT INTO #tmp values(6,'2017-09-05 13:32:00.000','2017-09-11 14:49:00.000')
INSERT INTO #tmp values(7,'2017-09-05 13:17:00.000','2017-09-12 21:00:00.000')
INSERT INTO #tmp values(8,'2017-09-05 23:11:00.000','2017-09-06 07:38:00.000')
INSERT INTO #tmp values(9,'2017-09-05 11:35:00.000','2017-09-14 16:12:00.000')
INSERT INTO #tmp values(10,'2017-09-05 14:05:00.000','2017-09-11 16:30:00.000')
DECLARE
#StartDate DATE = '09/01/2017'
,#EndDate DATE = '10/01/2017'
, #nHours INT = 9
;WITH d(OrderDate) AS
(
SELECT DATEADD(DAY, n-1, #StartDate)
FROM (SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects) AS x(n)
)
, CTE AS(
select OrderDate, t2.*
from #tmp t2
cross apply(select orderdate from d ) d
where StartDte >= #StartDate and EndDte <= #EndDate)
select OrderDate,
SUM(CASE WHEN OrderDate >= StartDte and OrderDate <= EndDte THEN 1 ELSE 0 END) [No. Beds Occupied at 00:00],
SUM(CASE WHEN StartDTE <= DateAdd(hour,#nHours,CAST(OrderDate as datetime)) and DateAdd(hour,#nHours,CAST(OrderDate as datetime)) <= EndDte THEN 1 ELSE 0 END) [No. Beds Occupied at 09:00]
from CTE
GROUP BY OrderDate
This should allow you to check for any hour of the day using the #nHours parameter if you so choose. If you only want to see records that actually fall within your date range then you can filter the cross apply on start and end dates.

SQL - Conditional column selection in join

I am not sure if this scenario can be achieved using TSQL. I have a table called WorkingDays, which have this info
ID | EmployeeId | Monday | Tuesday | Wednesday | Thursday | Friday
----------------------------------------------------------------------
1 | 1 | 2 | 2 | 3 | 6 | 5
2 | 2 | 1 | 7 | 5 | 2 | 3
The days columns store Ids of WorkingSchedule table, which has this columns:
ID int Primary Key
StartTime time
EndTime time
So what I need id get the StartTime and EndTime of an employee depending on the current date.
What I need to get from query is the start and end time depending on the day. The day I want to filter is de current date (using getdate() function)
So need to select the correct day column name to make the join.
How can I achieve this scenario?
The dynamic sql version:
declare #sql nvarchar(max) ='
select
t.EmployeeId
, StarTime = max(case when t.rn=1 then '+quotename(datename(weekday,getdate()))+' end)
, EndTime = max(case when t.rn=2 then '+quotename(datename(weekday,getdate()))+' end)
from (
select *
, rn = row_number() over (partition by t.EmployeeId order by t.Id)
from t
) t
group by t.EmployeeId;'
exec sp_executesql #sql;
rextester demo: http://rextester.com/WNH34961
returns:
+------------+----------+---------+
| EmployeeId | StarTime | EndTime |
+------------+----------+---------+
| 1 | 5 | 3 |
+------------+----------+---------+
Depending on how you want the output, here are two other ways that do not use dynamic sql:
Both use cross apply() to unpivot the data, and WorkDay = datename(weekday,getdate()) to get the current WorkDay column.
For one row output we add some conditional aggregation:
/* one row per employeeId */
select
t.EmployeeId
, x.WorkDay
, StarTime = max(case when t.rn=1 then x.Time end)
, EndTime = max(case when t.rn=2 then x.Time end)
from (
select *
, rn = row_number() over (partition by t.EmployeeId order by t.Id)
from t
) t
cross apply (values
('Monday',Monday),('Tuesday',Tuesday),('Wednesday',Wednesday)
,('Thursday',Thursday),('Friday',Friday)
) x (WorkDay,Time)
where WorkDay = datename(weekday,getdate())
group by t.EmployeeId, x.WorkDay
returns:
+------------+---------+----------+---------+
| EmployeeId | WorkDay | StarTime | EndTime |
+------------+---------+----------+---------+
| 1 | Friday | 5 | 3 |
+------------+---------+----------+---------+
If you want the output on two rows, like your current output:
/* two rows per employeeId */
select
t.Id
, t.EmployeeId
, x.WorkDay
, t.StartEnd
, x.Time
from (
select *
, StartEnd = case
when row_number() over (partition by t.EmployeeId order by t.Id) = 1
then 'StartTime'
else 'EndTime'
end
from t
) t
cross apply (values
('Monday',Monday),('Tuesday',Tuesday),('Wednesday',Wednesday)
,('Thursday',Thursday),('Friday',Friday)
) x (WorkDay,Time)
where WorkDay = datename(weekday,getdate());
returns:
+----+------------+---------+-----------+------+
| Id | EmployeeId | WorkDay | StartEnd | Time |
+----+------------+---------+-----------+------+
| 1 | 1 | Friday | StartTime | 5 |
| 2 | 1 | Friday | EndTime | 3 |
+----+------------+---------+-----------+------+
select wd.Employee, ws.StartTime, ws.EndTime
from WorkingDays wd
join WorkingSchedule ws on ws.Id = case datename(weekday, getdate())
when 'Monday' then ws.Monday
when 'Tuesday' then ws.Tuesday
when 'Wednesday' then ws.Wednesday
when 'Thursday' then ws.Thursday
when 'Friday' then ws.Friday
else 0
end
Hint: datename(weekday, getdate()) returns you the weekday name in your current locale! This might be better:
select wd.Employee, ws.StartTime, ws.EndTime
from WorkingDays wd
join WorkingSchedule ws on ws.Id = case datepart(weekday, getdate())
when 1 then wd.Monday
when 2 then wd.Tuesday
when 3 then wd.Wednesday
when 4 then wd.Thursday
when 5 then wd.Friday
else 0
end
But then you have to check which day is the first of week (0, 1), depending on your settings.

SQL query to fetch data based on date range

i want to fetch data for below scenario
input: (Let say today is: 1-Mar-2015)
LicenseNo LicenseEndDate LicenseType Amount
1 1-Apr-2015 AB 100
2 5-Apr-2015 AB 150
3 7-Apr-2015 BC 200
4 10-July-2015 AB 120
5 10-july-2015 BC 140
Expected O/P
AB BC
Between 0-3 months 250 200
Between 3-6 months 120 140
this may increase
SELECT 'Between 0-3 months',
SUM(Case when l.LicenseType='AB' then l.Amount End),
SUM(Case when l.LicenseType='BC' then l.Amount End)
FROM licence l
WHERE l.LicenceEndDate BETWEEN #inputDate AND DATEADD (month , 3 , #inputDate)
UNION
SELECT 'Between 3-6 months',
SUM(Case when l.LicenseType='AB' then l.Amount End),
SUM(Case when l.LicenseType='BC' then l.Amount End)
FROM licence l
WHERE l.LicenceEndDate BETWEEN DATEADD (month , 3 , #inputDate) AND DATEADD (month , 6 , #inputDate)
Union of two queries for the two interval.
Or you can create a temporary table based on your input date like this
| ID | DESCRIPTION | DATA_MIN | DATA_MAX |
| 1 | Between 0-3 months | #input | #input + 3|
| 2 | Between 3-6 months | #input +3| #input + 6|
And use that for your join
Having the derived table in this solution just makes grouping easier in the outer select, it saves us from repeating ourselves too much. The SUM(case...end) structure is one way of pivoting our results, you could look at the pivot operator but I think it is overkill for this scenario. I also added a few other cases even though your supplied data doesn't use them because I figure they are likely. you can always add a where clause if you are looking at specific groups and this is facilitated by the derived table as well.
I have used GETDATE() but you can substitute a date variable for that if it suits better.
declare #t as table
(
LicenseNo int,
LicenseEndDate datetime,
LicenseType varchar(2),
Amount numeric(10,2)
)
insert into #t
values
(1,'1-Apr-2015','AB',100),
(2,'5-Apr-2015','AB',150),
(3,'7-Apr-2015','BC',200),
(4,'10-July-2015','AB',120),
(5,'10-july-2015','BC',140)
declare #comparison_date as datetime = getdate()
select
case ExpGrp
when 0 then 'Expired'
when 1 then 'Expires today'
when 2 then 'Expires in 0-3 months'
when 3 then 'Expires in 3-6 months'
when 4 then 'Not due to expire'
else 'Something went wrong'
end as Descrip,
sum(case when LicenseType = 'AB'
then Amount
else 0
end) as AB,
sum(case when LicenseType = 'BC'
then Amount
else 0
end) as BC
from
(select *,
case
when LicenseEndDate < #comparison_date
then 0
when LicenseEndDate = #comparison_date
then 1
when LicenseEndDate > #comparison_date and LicenseEndDate <= dateadd(MONTH,3,#comparison_date)
then 2
when LicenseEndDate > dateadd(MONTH,3,#comparison_date) and LicenseEndDate <= dateadd(MONTH,6,#comparison_date)
then 3
else 4
end as ExpGrp
from #t) t
group by t.ExpGrp

Complex SQL query

I have a table that tracks emails sent from applications on my server. I would like to write a query that shows how many emails were sent by each application in a certain time period. Here is the table:
----------------------------------------------------------
| emailID | SentDT | ApplicationName |
----------------------------------------------------------
| 1 | 2011-08-04 14:43:31.080 | Term Form |
----------------------------------------------------------
| 2 | 2011-08-04 13:59:46.062 | Term Form |
----------------------------------------------------------
| 3 | 2011-08-03 10:38:15.015 | Request Form |
----------------------------------------------------------
| 4 | 2011-08-03 05:52:29.005 | Term Form |
----------------------------------------------------------
| 5 | 2011-08-01 19:58:31.094 | Recruiting Form |
----------------------------------------------------------
I would like to see number of emails sent Today, Last 24 hours, Last 7 days, This Month, Last Month, All time.
I know how to do each of these queries by themselves, but I have no clue how to do it in one trip to the database.
For example:
--------------------------------------------------------------
| ApplicationName | Today | Last24 | Last7days | ThisMonth |
--------------------------------------------------------------
| Term Form | 2 | 5 | 10 | 19 |
--------------------------------------------------------------
| Request Form | 9 | 18 | 36 | 75 |
--------------------------------------------------------------
| Recruiting Form | 15 | 35 | 100 | 250 |
--------------------------------------------------------------
I tried using a nested select for each subset of times, but I can't use a group by in the nested select. My query that doesn't produce results:
select COUNT(emailID), ApplicationName, (select COUNT(emailID) from emaillog where SentDT > '08/02/2011') as TwoDaysAgo
from emaillog
group by ApplicationName
order by ApplicationName
I think it's much easier to do all the date calculations up front, then you can refer to local variables with logical names instead of embedding all the datediff/case etc. calculations in the query logic.
Made a couple of assumptions here. (1) that no data in EmailLog is in the future (2) that by "Last 7 days" you mean today and the full 6 days preceding. I've also included a grand total - even though it's not listed in your desired output, it seems you were trying to get it with the COUNT() outside the subquery.
DECLARE #now SMALLDATETIME = SYSDATETIME();
DECLARE #today DATE = #now,
#24hrsago SMALLDATETIME = DATEADD(DAY, -1, #now);
DECLARE #7daysago DATE = DATEADD(DAY, -6, #today),
#ThisMonth DATE = DATEADD(DAY, 1-DATEPART(DAY, #today), #today);
--SELECT #now, #today, #24hrsago, #7daysago, #ThisMonth;
WITH d AS
(
SELECT ApplicationName, c = COUNT(*)
FROM EmailLog
GROUP BY ApplicationName
),
g AS
(
SELECT
ApplicationName,
[Today] = SUM(CASE WHEN SentDt >= #today THEN 1 ELSE 0 END),
[Last24] = SUM(CASE WHEN SentDt >= #24hrsago THEN 1 ELSE 0 END),
[Last7Days] = SUM(CASE WHEN SentDt >= #7daysago THEN 1 ELSE 0 END),
[ThisMonth] = SUM(CASE WHEN SentDt >= #ThisMonth THEN 1 ELSE 0 END)
FROM EmailLog
GROUP BY ApplicationName
)
SELECT d.ApplicationName,
Total = d.c,
[Today] = COALESCE(g.[Today], 0),
[Last24] = COALESCE(g.[Last24], 0),
[Last7days] = COALESCE(g.Last7days, 0),
[ThisMonth] = COALESCE(g.ThisMonth, 0)
FROM d LEFT OUTER JOIN g
ON d.ApplicationName = g.ApplicationName;
EDIT
If my assumption was wrong and you don't need the total count by application name, the query becomes much simpler:
DECLARE #now SMALLDATETIME = SYSDATETIME();
DECLARE #today DATE = #now,
#24hrsago SMALLDATETIME = DATEADD(DAY, -1, #now);
DECLARE #7daysago DATE = DATEADD(DAY, -6, #today),
#ThisMonth DATE = DATEADD(DAY, 1-DATEPART(DAY, #today), #today);
SELECT ApplicationName,
[Today] = SUM(CASE WHEN SentDt >= #today THEN 1 ELSE 0 END),
[Last24] = SUM(CASE WHEN SentDt >= #24hrsago THEN 1 ELSE 0 END),
[Last7Days] = SUM(CASE WHEN SentDt >= #7daysago THEN 1 ELSE 0 END),
[ThisMonth] = SUM(CASE WHEN SentDt >= #ThisMonth THEN 1 ELSE 0 END)
FROM EmailLog
GROUP BY ApplicationName;
Ordering optional of course.
try:
Select ApplicationName, COunt(*) numEmails
From table
where SentDT Between #startDateTime and #EndDateTime
Group By ApplicationName
NOTE: startDateTime and EndDateTime are oundary limits on records to be processed.
if you also want to establish buckets around specified datetiome ranges, you simply need to define those datetime range buckets in another group by expression (and output that same expression in the select clause ... as an example, say the datetime ranges are calendar months...
Select DateAdd(month, DateDiff(month, 0, SentDT), 0) CalMonth,
ApplicationName, Count(*) numEmails
From table
where SentDT Between #startDateTime and #EndDateTime
Group By DateAdd(month, DateDiff(month, 0, SentDT), 0),
ApplicationName
Something like this should do the trick
select
ApplicationName,
sum(case when daterange = 0 then cnt else 0 end) as Today,
sum(case when daterange = 1 then cnt else 0 end) as yesterday,
sum(case when daterange <=2 then cnt else 0 end) as Week,
sum(case when daterange <=3 then cnt else 0 end) as month,
sum(cnt) as AllTime
from
(select
ApplicationName,
case
when days = 0 then '0'
when days = 1 then '1'
when days <= 7 then '2'
when days <= 30 then '3'
else 4
end as
DateRange,
Count(emailid) cnt
from
(select ApplicationName, EmailID, datediff(dd, SentDT, getdate()) as Days
from
dbo.[YourTableGoesHere]
) as foo
Group by
ApplicationName,
case when days < 1 then '0'
when days = 1 then '1'
when days <= 7 then '2'
when days <= 30 then '3'
else 4
end) as bar
group by
ApplicationName