SQL query to fetch data based on date range - sql

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

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)

How to achieve the bucket values in SQL?

I have schedule table like this (table name = testSch)
ID Amount scheduleDate
1 7230.00 2018-07-13
1 7272.00 2018-07-27
1 7314.00 2018-08-10
1 7356.00 2018-08-24
1 7398.00 2018-09-07
1 7441.00 2018-09-21
1 7439.00 2018-10-08
1 7526.00 2018-10-22
1 7570.00 2018-11-05
1 7613.00 2018-11-19
1 5756.00 2018-12-03
I need to sum the Amount field based on specific bucket values as shown below
Principal_7To30_Days
Principal_1To3_Months
Principal_3To6_Months
Principal_6To12_Months
Principal_1To3_Years
by giving an input date
And my input date is 2018-07-09 and below is my query;
;with cteSchedule as (
select *,DATEDIFF(DAY,'20180709',scheduleDate) as datedifference,
DATEDIFF(MONTH,'20180709',scheduleDate) as monthdifference from testSch)
select ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.datedifference <7),0) AS Principal_0To7_Days,
ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.datedifference>=7 and cteSchedule.datedifference<30),0)
AS Principal_7To30_Days,
ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.datedifference>=30 and cteSchedule.datedifference<90),0) AS Principal_1To3_Months,
ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.datedifference>=90 and cteSchedule.datedifference<180),0) AS Principal_3To6_Months,
ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.datedifference>=180 and cteSchedule.datedifference<365),0) AS Principal_6To12_Months
And below is my output
Principal_0To7_Days Principal_7To30_Days Principal_1To3_Months Principal_3To6_Months Principal_6To12_Months
7230.00 7272.00 29509.00 35904.00 0.00
But the correct output should be
Principal_0To7_Days Principal_7To30_Days Principal_1To3_Months Principal_3To6_Months Principal_6To12_Months
7230.00 7272.00 36948.00 28465.00 0.00
So the problem is i'm getting wrong values for Principal_1To3_Months and Principal_3To6_Months, When I asked my client how do they calculate this in their legacy system, they replied that they calculate using +-months by adding number of months and not days. So if today is 2018-07-09 + 3 months we will get 2018-10-09.
So I used the month difference in my cte query as below
DATEDIFF(MONTH,'20180709',scheduleDate) as monthdifference
And use this in my overall query as below
ISNULL((SELECT SUM(cteSchedule.Amount)
FROM cteSchedule
WHERE cteSchedule.monthdifference>=1 and cteSchedule.monthdifference<=3),0) AS Principal_1To3_Months
But this time also I get the same values as mentioned in my very first output.
Can someone please point out where is my mistake and how to achieve this values as mentioned in correct output
I wouldn't use DATEDIFF to calculation day or month difference days, because there is some month have 31 days, others month have 30 days.
Therefore, the calculated difference days are not accurate.
I would use DATEADD instead of DATEDIFF to do the condition.
;with cteSchedule as (
select *,'20180709' compareDay
from testSch
)
SELECT Sum(CASE
WHEN t.scheduleDate < DATEADD(day, 7, compareDay)
THEN t.amount
ELSE 0
END) AS Principal_0To7_Days,
Sum(CASE
WHEN t.scheduleDate >=DATEADD(day, 7, compareDay) AND t.scheduleDate < DATEADD(day, 30, compareDay)
THEN t.amount
ELSE 0
END) AS Principal_7To30_Days,
Sum(CASE
WHEN t.scheduleDate >=DATEADD(month,1,compareDay) AND t.scheduleDate < DATEADD(month,3,compareDay)
THEN t.amount
ELSE 0
END) AS Principal_1To3_Months,
Sum(CASE
WHEN t.scheduleDate >=DATEADD(month,3,compareDay) AND t.scheduleDate < DATEADD(month,6,compareDay)
THEN t.amount
ELSE 0
END) AS Principal_3To6_Months,
Sum(CASE
WHEN t.scheduleDate >=DATEADD(month,6,compareDay) AND t.scheduleDate < DATEADD(month,12,compareDay)
THEN t.amount
ELSE 0
END) AS Principal_6To12_Months
from cteSchedule t
SQLFiddle
[Results]:
| Principal_0To7_Days | Principal_7To30_Days | Principal_1To3_Months | Principal_3To6_Months | Principal_6To12_Months |
|---------------------|----------------------|-----------------------|-----------------------|------------------------|
| 7230 | 7272 | 36948 | 28465 | 0 |
Note
You can use CASE WHEN with SUM
Aggregate function instead of select subquery, the performance will be better.

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.

SSRS Report shows data for different dates

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.

T-SQL Query to count the occurrences of different conditions in table

DBMS: SQL Server 2008
I have a table with the below structure, which represents tender applications by a particular vendor in different companies and the status of their application. Decision R = Reject, A = Accept.
---------------------------------------------------------------
| ID | Company | ApplicationDate | Decision | DecisionDate |
---------------------------------------------------------------
| 1 | ABC | 15/03/2011 | A | 17/04/2011 |
| 2 | ABC | 23/05/2012 | R | 01/03/2014 |
| 3 | XYZ | 14/07/2012 | R | 20/07/2012 |
| 4 | ABC | 18/01/2013 | A | 24/02/2013 |
| 5 | XYZ | 12/08/2013 | R | 11/09/2013 |
| 6 | ABC | 30/09/2013 | R | 14/10/2013 |
| 7 | ABC | 08/01/2014 | A | 08/06/2014 |
| 8 | ABC | 10/05/2014 | A | 19/05/2014 |
---------------------------------------------------------------
*Dates are in time-stamp format. Dates in the example table (dd/mm/yyyy) are for representation purpose only.
What I need to mine from this simple database is,
Number of tenders applied in the last 12 months - assuming 11/07/2014 as the current date.
Number of tenders rejected in the last 12 months.
Time in months since the last tender application.
Time in months since the last tender rejection.
Number of tenders applied in ABC in the last 12 months.
Number of tenders rejected in ABC in the last 12 months.
Time in months since the last tender application to ABC.
Time in months since the last tender rejection by ABC.
So based on the given table data, the statistics would be,
Four. (IDs 5, 6, 7 and 8 have application date with in 12 months of today)
Three (IDs 2, 5 and 6 have decision date with in 12 months and decision is R)
Two (10/05/2014 till today)
Four (ID 2's rejection was on 01/03/2014)
Three (IDs 6, 7 and 8)
Two (IDs 2 and 6)
Two (10/05/2014 till today)
Four (ID 2's rejection was on 01/03/2014)
Is there a way to get these stats using a single query on the table (possibly by using Sum with case)?
What I have so far is as below.
SELECT
SUM(CASE WHEN DATEDIFF(MM, ApplicationDate, GETDATE()) <= 12 THEN 1 ELSE 0 END) 'Total Tenders',
SUM(CASE WHEN DATEDIFF(MM, DecisionDate, GETDATE()) <= 12 AND DECISION = R THEN 1 ELSE 0 END) 'Total Rejects'
SUM(CASE WHEN DATEDIFF(MM, ApplicationDate, GETDATE()) <= 12 AND Company = 'ABC' THEN 1 ELSE 0 END) 'Total Tenders To ABC',
SUM(CASE WHEN DATEDIFF(MM, DecisionDate, GETDATE()) <= 12 AND DECISION = R AND Company = 'ABC' THEN 1 ELSE 0 END) 'Total Rejects By ABC'
FROM TenderTable;
That gives me 1, 2, 5 and 6 of the required stats.
You can use WITH ROLLUP
SET DATEFORMAT 'dmy'
DECLARE #tbl TABLE (ID INT, Company VARCHAR(3), ApplicationDate DATE, Decision CHAR(1), DecisionDate DATE)
INSERT INTO #tbl
(ID, Company, ApplicationDate, Decision, DecisionDate)
VALUES
(1,'ABC','15/03/2011','A','17/04/2011'),
(2,'ABC','23/05/2012','R','01/03/2014'),
(3,'XYZ','14/07/2012','R','20/07/2012'),
(4,'ABC','18/01/2013','A','24/02/2013'),
(5,'XYZ','12/08/2013','R','11/09/2013'),
(6,'ABC','30/09/2013','R','14/10/2013'),
(7,'ABC','08/01/2014','A','08/06/2014'),
(8,'ABC','10/05/2014','A','19/05/2014')
SELECT
Company = CASE WHEN (GROUPING(Company) = 1) THEN 'ALL' ELSE ISNULL(Company, 'UNKNOWN') END,
TendersApplied = SUM(CASE WHEN ApplicationDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) THEN 1 END),
TendersRejected = SUM(CASE WHEN DecisionDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) AND Decision = 'R' THEN 1 END),
MonthsSinceLastTenderApplication = DATEDIFF(M, MAX(ApplicationDate), GETDATE()),
MonthsSinceLastTenderRejection = DATEDIFF(M, MAX(CASE WHEN Decision = 'R' THEN DecisionDate END), GETDATE())
FROM #tbl
GROUP BY Company
WITH ROLLUP
HAVING GROUPING(Company) = 1
OR Company = 'ABC'
ORDER BY GROUPING(Company), Company
Which produces
Company TendersApplied TendersRejected MonthsSinceLastTenderApplication MonthsSinceLastTenderRejection
------- -------------- --------------- -------------------------------- ------------------------------
ABC 3 2 2 4
ALL 4 3 2 4
Edit by Questioner:
Modification to the query above satisfies the requirements.
SELECT
TendersApplied = SUM(CASE WHEN ApplicationDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) THEN 1 END),
TendersRejected = SUM(CASE WHEN DecisionDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) AND Decision = 'R' THEN 1 END),
MonthsSinceLastTenderApplication = DATEDIFF(M, MAX(ApplicationDate), GETDATE()),
MonthsSinceLastTenderRejection = DATEDIFF(M, MAX(CASE WHEN Decision = 'R' THEN DecisionDate END), GETDATE()),
TendersAppliedABC = SUM(CASE WHEN Company = 'ABC' AND ApplicationDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) THEN 1 END),
TendersRejectedABC = SUM(CASE WHEN Company = 'ABC' AND DecisionDate >= DATEADD(M, -12, CAST(GETDATE() AS DATE)) AND Decision = 'R' THEN 1 END),
MonthsSinceLastTenderApplicationABC = DATEDIFF(M, MAX(CASE WHEN Company = 'ABC' THEN ApplicationDate END), GETDATE()),
MonthsSinceLastTenderRejectionABC = DATEDIFF(M, MAX(CASE WHEN Company = 'ABC' AND Decision = 'R' THEN DecisionDate END), GETDATE())
FROM #tbl
You may be overlooking an simple way that is not obvious if you've not seen it before.
For example, I have a Table Named StateCounty that has State and County data
select count(*) as TotalCounties
, (select count(*) from StateCounty where StateCode = 'AK') as AlaskaCounties
, (select count(*) from StateCounty where StateCode = 'TX') as TexasCounties
, (select count(*) from StateCounty where County = 'Marion') as CountiesNamedMarion
, (select count(*) from StateCounty where County = 'Washington') as CountiesNamedWashington
from StateCounty
Yields the output
TotalCounties AlaskaCounties TexasCounties CountiesNamedMarion CountiesNamedWashington
------------- -------------- ------------- ------------------- -----------------------
3131 17 254 17 31
(1 row(s) affected)
I believe you can write the query you want yourself now.