Related
I have the following data in a SQL table:
+------------------------------------+
| ID YEARS START_DATE |
+------------------------------------+
| ----------- ----------- ---------- |
| 1 5 2020-12-01 |
| 2 8 2020-12-01 |
+------------------------------------+
Trying to create a SQL that would expand the above data and give me a start and end date for each year depending on YEARS and START_DATE from above table. Sample output below:
+-----------------------------------------------+
| ID YEAR DATE_START DATE_END |
+-----------------------------------------------+
| ----------- ----------- ---------- ---------- |
| 1 1 2020-12-01 2021-11-30 |
| 1 2 2021-12-01 2022-11-30 |
| 1 3 2022-12-01 2023-11-30 |
| 1 4 2023-12-01 2024-11-30 |
| 1 5 2024-12-01 2025-11-30 |
| 2 1 2020-12-01 2021-11-30 |
| 2 2 2021-12-01 2022-11-30 |
| 2 3 2022-12-01 2023-11-30 |
| 2 4 2023-12-01 2024-11-30 |
| 2 5 2024-12-01 2025-11-30 |
| 2 6 2025-12-01 2026-11-30 |
| 2 7 2026-12-01 2027-11-30 |
| 2 8 2027-12-01 2028-11-30 |
+-----------------------------------------------+
I would use an inline tally for this, as they are Far faster than a recursive CTE solution. Assuming you have low values for Years:
WITH YourTable AS(
SELECT *
FROM (VALUES(1,5,CONVERT(date,'20201201')),
(2,8,CONVERT(date,'20201201')))V(ID,Years, StartDate))
SELECT ID,
V.I + 1 AS [Year],
DATEADD(YEAR, V.I, YT.StartDate) AS StartDate,
DATEADD(DAY, -1, DATEADD(YEAR, V.I+1, YT.StartDate)) AS EndDate
FROM YourTable YT
JOIN (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))V(I) ON YT.Years > V.I;
If you have more than 10~ years you can use either create a tally table, or create an large one inline in a CTE. This would start as:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I --remove the -1 if you don't want to start from 0
FROM N N1, N N2) --100 rows, add more Ns for more rows
...
Of course, I doubt you have 1,000 of years of data.
You can use a recursive CTE:
with cte as (
select id, 1 as year, start_date,
dateadd(day, -1, dateadd(year, 1, start_date)) as end_date,
years as num_years
from t
union all
select id, year + 1, dateadd(year, 1, start_date),
dateadd(day, -1, dateadd(year, 1, start_date)) as end_date,
num_years
from cte
where year < num_years
)
select id, year, start_date, end_date
from cte;
Here is a db<>fiddle.
In a query, you can use the following:
DATEADD(YEAR, 1, DATE_START) - 1
to add this to the table you can just create the extra column, and set it equal to the value of the above, e.g.
UPDATE MyTable
SET DATE_END = DATEADD(YEAR, 1, DATE_START) - 1
If you are working with sql server, then you can try to use operator CROSS APPLY with master.dbo.spt_values table to get list of numbers and generate dates:
select ID,T.number+1 as YEAR,
--generate date_start using T.number
dateadd(year,T.number,START_DATE)date_start,
--generate end_date: adding 1 year to start date
dateadd(dd,-1,dateadd(year,1,dateadd(year,T.number,START_DATE)))date_end
from Table
cross apply
master.dbo.spt_values T
where T.type='P' and T.number<YEARS
I don't really know how to make a MySQL query where I get all the data(Columns) of the most present month + year.
Being a little bit more specific I need to group the columns (description, data1, data2, data3) and only get the one that has the highest month + year.
The table contains data like this one:
description data1 data2 data3 year month adddate
desc1 0 7 1 2019 5 2019-05-23
desc2 0 7 1 2019 5 2019-05-23
desc3 1 7 1 2019 5 2019-05-23
desc4 0 2 1 2018 12 2019-05-23
I've tried using max on the month, year and adddate.
select description, data1, data2, data3, max(year) as year, max(month) as month, max(adddate) as adddate
from tabledata
group by description, data1, data2, data3
But with this I'm getting that the max register is desc2 with the month 12 which is not correct since the month is desc2 is 6.
You need to get the max value of an expression like:
100 * year + month
or
12 * year + month
So do this:
select *
from tabledata
where 100 * year + month = (
select max(100 * year + month)
from tabledata
)
See the demo.
Results:
> description | data1 | data2 | data3 | year | month | adddate
> :---------- | ----: | ----: | ----: | ---: | ----: | :---------
> desc1 | 0 | 7 | 1 | 2019 | 5 | 23/05/2019
> desc2 | 0 | 7 | 1 | 2019 | 5 | 23/05/2019
> desc3 | 1 | 7 | 1 | 2019 | 5 | 23/05/2019
You need to find the max combination of year and month (MaxYearMonth) and then join to your table again to find all the rows that match that MaxYearMonth. Here is the solution for SQL Server...
IF OBJECT_ID('tempdb.dbo.#tabledata', 'U') IS NOT NULL DROP TABLE #tabledata;
CREATE TABLE #tabledata
(
description VARCHAR(10)
, data1 INT
, data2 INT
, data3 INT
, year INT
, month INT
, adddate DATE
);
INSERT INTO #tabledata VALUES ('desc1', 0, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc2', 0, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc3', 1, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc4', 0, 2, 1, 2018, 12, '2019-05-23')
select a.description, a.data1, a.data2, a.data3
from #tabledata a
inner join
(
select max(convert(char(4), year) + right('0' + convert(varchar(2), month), 2)) as [MaxYearMonth]
from #tabledata
) b on convert(char(4), a.year) + right('0' + convert(varchar(2), a.month), 2) = b.MaxYearMonth
Yields this...
description data1 data2 data3
----------- ----------- ----------- -----------
desc1 0 7 1
desc2 0 7 1
desc3 1 7 1
Click here to see it in action.
You mention "mysql" so if you are really using mySQL then the syntax will likely need to change a bit.
Let's assume I have a table structure like this.
CheckIn
- int checkInId pk
- int companyPositionId
- Date checkInDate
Let's say I want to get a count of all check ins for the last 7 days from a given date. What would be the best way to do this? Right now I'm making a query for each of the 7 dates AND the company positions. Currently this is too slow because there could be many companyPositions * 7 days. How can I roll this up into one query?
Would it be easiest to generate the last 7 days dates and construct a long query? Could I then group count by a date range for each of the 7 days?
An ideal result back could look like:
companyPositionId, date1Count, date2Count, date3Count, date4Count, date5Count, date6Count.
Example Data:
checkInId | companyPositionId | checkInDate
1 | 1 | 1970-01-01
2 | 1 | 1970-01-02
3 | 1 | 1970-01-03
4 | 1 | 1970-01-04
5 | 1 | 1970-01-05
6 | 1 | 1970-01-06
7 | 1 | 1970-01-07
8 | 2 | 1970-01-01
9 | 2 | 1970-01-02
10 | 2 | 1970-01-03
11 | 2 | 1970-01-04
12 | 2 | 1970-01-05
13 | 2 | 1970-01-06
14 | 2 | 1970-01-07*
15 | 2 | 1970-01-07*
My current query is this:
SELECT * FROM CheckIn
WHERE (startDate) <= (inputDate)
AND (inputDate) <= (endDate)
AND companyPositionId = (companyPositionId);
I then loop through each startDate/endDate that is generated from the beginning of the day and end of that day. And then each of the companyPositionId's.
Ideal result:
companyPositionId | date1Count | date2Count | date3Count | date4Count | date5Count | date6Count | date7Count
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1
2 | 1 | 1 | 1 | 1 | 1 | 1 | 2
You can do this with the PIVOT command, or with conditional SUMs:
DECLARE #my_date DATE = CAST(GETDATE() AS DATE)
SELECT
companyPositionId,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -7, #my_date) THEN 1 ELSE 0 END) AS date1Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -6, #my_date) THEN 1 ELSE 0 END) AS date2Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -5, #my_date) THEN 1 ELSE 0 END) AS date3Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -4, #my_date) THEN 1 ELSE 0 END) AS date4Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -3, #my_date) THEN 1 ELSE 0 END) AS date5Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -2, #my_date) THEN 1 ELSE 0 END) AS date6Count,
SUM(CASE WHEN CAST(checkInDate AS DATE) = DATEADD(DAY, -1, #my_date) THEN 1 ELSE 0 END) AS date7Count
FROM
CheckIn
WHERE
checkInDate BETWEEN DATEADD(DAY, -7, GETDATE()) AND DATEADD(DAY, -1, GETDATE())
GROUP BY
companyPositionId
If your checkInDate has a time component then you'll need to account for that.
I declared the #my_date variable just to avoid having to repeat that expression in the query a bunch of times, but you could replace the variable with that expression and it would work as well. You could also use BETWEEN which might have better performance since the optimizer could then potentially use an index on your checkInDate. Just calculate midnight/11:59:59 of each of the days instead of looking for equality.
Look at
Declare #d date= '2016-05-05'; -- parameter, we need this day and six previous
Select
companyPositionId
,date1Count = count(case checkInDate when #d then checkInId end)
,date2Count = count(case checkInDate when dateadd(d,-1,#d) then checkInId end)
--...
from checkIn
where checkInDate between dateadd(d,-6,#d) and #d
group by companyPositionId
order by companyPositionId;
Here's a quick pivot example assuming you're looking for the previous 7 days of the #inputDate. This should work even with the time component since it goes off a DATEDIFF with days only.
DECLARE #InputDate DATE = '1/8/1970'
;WITH CTE AS (
SELECT *, DATEDIFF(DD, checkInDate, #InputDate) AS DaysAgo FROM #CheckIns
)
SELECT
companyPositionId,
SUM([7]) AS date1Count,
SUM([6]) AS date2Count,
SUM([5]) AS date3Count,
SUM([4]) AS date4Count,
SUM([3]) AS date5Count,
SUM([2]) AS date6Count,
SUM([1]) AS date7Count -- Reversed columns since you wanted 7 to be most recent day
FROM CTE
PIVOT (COUNT(checkInId) FOR DaysAgo IN ([1], [2], [3], [4], [5], [6], [7])) PVT
GROUP BY
companyPositionId
This gives the following results matching your desired output:
companyPositionId date1Count date2Count date3Count date4Count date5Count date6Count date7Count
----------------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 2
Create Data:
CREATE TABLE #checkin (
checkInId INT IDENTITY(1, 1),
companyPositionId int,
checkInDate DATE
)
DECLARE #counter INT = 100
WHILE #counter > 0
BEGIN
INSERT INTO #checkin
( companyPositionId, checkInDate )
VALUES ( RAND() * 10 + 1, -- 10 possible companies?
DATEADD(day, RAND()*-7, GETDATE()) -- random days in the last 2 weeks
)
SET #counter = #counter - 1
END
Logic starts here:
DECLARE
#now DATETIME = GETDATE(),
#days INT = -7 -- Our interval of interest
This logic is partially borrowed from Bogdan Sahlean, his results were getting all '1' for me, so I changed the dayNum calculation
SELECT *
FROM (
SELECT companyPositionId, checkInId, 1 + CAST(DATEDIFF(DAY,checkInDate, #now) AS INT) AS DayNum
FROM #CheckIn
WHERE checkInDate BETWEEN CAST(DATEADD(DAY, #days, #now) AS DATE) AND #now
) AS ps -- Pivot source
PIVOT ( COUNT(checkInId) FOR DayNum IN ([1], [2], [3], [4], [5], [6], [7]) ) AS p
DROP TABLE #checkin
I have a table with, for example this data
ID |start_date |end_date |amount
---|------------|-----------|-------
a1 |2013-12-01 |2014-03-31 |100
Iwant to have a query that split the dates so I have the amount splitted out over the year like this :
ID |org_start_date |org_end_date |new_start_date |new_end_date |amount
---|----------------|---------------|----------------|----------------|-------
a1 |2013-12-01 |2014-03-31 |2013-12-01 |2013-12-31 |25
a1 |2013-12-01 |2014-03-31 |2014-01-01 |2014-03-31 |75
The 25 in 2013 is because 2013 has one month and 75 in 2014 because this has 3 months
Is there a way to do this in T-SQL?
Thx in advance!
Use spt_values table to create a calendar table, then join to your table to split date range into any part you want.
If split by year and divide amount by months you could:
with dates as
(
select number,DATEADD(day,number,'20130101') as dt
from master..spt_values
where number between 0 and 1000 AND TYPE='P'
)
select
m.start_date as org_start_date,
m.end_date as org_end_date,
min(d.dt) as new_start_date,
max(d.dt) as new_end_date,
m.amount*count(distinct month(d.dt))/(datediff(month,m.start_date,m.end_date)+1) as amount
from
MonthSplit m
join
dates d
on
d.dt between m.start_date and m.end_date
group by
m.start_date, m.end_date, year(d.dt),m.amount
Here is the SQL FIDDLE DEMO.
Here is a solution using a numbers table:
SQL Fiddle Example
DECLARE #STARTYR INT = (SELECT MIN(YEAR([Start Date])) FROM Table1)
DECLARE #ENDYR INT = (SELECT MAX(YEAR([End Date])) FROM Table1)
SELECT [Id]
, #STARTYR + Number AS [Year]
, CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END AS [Start]
, CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE [End Date] END AS [End]
, DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END
,CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Months]
, DATEDIFF(MONTH,[Start Date],[End Date]) + 1 [Total Months]
, ([Amount] / (DATEDIFF(MONTH,[Start Date],[End Date]) + 1))
*
DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END
,CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Proportion]
FROM Numbers
LEFT JOIN Table1 ON YEAR([Start Date]) <= #STARTYR + Number
AND YEAR([End Date]) >= #STARTYR + Number
WHERE Number <= #ENDYR - #STARTYR
I don't have a ready made SQL for you but just a thought to solve this problem. If you have some experience with SQL it won't be hard to express it in SQL.
You could do this by defining a reference table for the months that are between your begin and end date:
ID | month | year | month start | month end | count |
-----------------------------------------------------------------------------
1001 | dec-2013 | 2013 | 1-12-2013 | 31-12-2013 | 1 |
1001 | jan-2014 | 2014 | 1-1-2014 | 31-1-2014 | 1 |
1001 | feb-2014 | 2014 | 1-2-2014 | 28-2-2014 | 1 |
1001 | mar-2014 | 2014 | 1-3-2014 | 31-3-2014 | 1 |
Maybe you already have such a time ref table in your DWH.
When you join (with a between statement) your table with the record that contains the start and end date per row, with this reference table, you'll have the split from 1 row to the number of months contained in the range correct. The count column will help you to get the split ratio per year right by grouping over the year afterwards (like : 1/4 for 2013 and 3/4 for 2014). You'll have to apply the ratio to the field 'amount' that you want to split up.
It's very similar to this question:
Split date range into one row per month in sql server
Although you are doing grouping by year, so based on that answer you can modify it to do what you want by adding MIN, MAX to your date values and grouping by the YEAR():
SQL Fiddle Demo
Schema Setup:
CREATE TABLE MonthSplit
([ID] varchar(2), [start_date] datetime, [end_date] datetime, [amount] int)
;
INSERT INTO MonthSplit
([ID], [start_date], [end_date], [amount])
VALUES
('a1', '2013-12-01 00:00:00', '2014-03-31 00:00:00', 100),
('a2', '2013-10-01 00:00:00', '2015-05-01 00:00:00', 400)
;
Recursive CTE to group by Year:
WITH cte AS
(SELECT ID
, start_date
, end_date
, start_date AS from_date
, DATEADD(day, day(start_date)* -1 + 1, start_date) AS first_of_month
FROM MonthSplit
UNION ALL
SELECT ID
, start_date
, end_date
, DATEADD(month,1,first_of_month)
, DATEADD(month,1,first_of_month)
FROM cte
WHERE DATEADD(month,1,from_date) < end_date
)
SELECT ID as ID,
min(start_date) as org_start_date,
min(end_date) as org_end_date,
min(from_date) AS new_start_date,
CASE when max(end_date) < DATEADD(month,1,max(first_of_month)) THEN
max(end_date)
ELSE
DATEADD(day, -1, DATEADD(month,1,max(first_of_month)))
END AS new_end_date
FROM cte
group by year(from_date), ID
Results:
| ID | ORG_START_DATE | ORG_END_DATE | NEW_START_DATE | NEW_END_DATE |
|----|-------------------|----------------|-------------------|-------------------|
| a1 | December, 01 2013 | March, 31 2014 | December, 01 2013 | December, 31 2013 |
| a1 | December, 01 2013 | March, 31 2014 | January, 01 2014 | March, 31 2014 |
| a2 | October, 01 2013 | May, 01 2015 | October, 01 2013 | December, 31 2013 |
| a2 | October, 01 2013 | May, 01 2015 | January, 01 2014 | December, 31 2014 |
| a2 | October, 01 2013 | May, 01 2015 | January, 01 2015 | April, 30 2015 |
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.