SQL Query to Count number of values in a column Per Month - sql

id date sales
1 01/01/2015 100
2 01/01/2015 100
3 02/01/2015 100
4 03/01/2015 100
What I need is to count the number of sales in a given date range (per month)which is based user input which are StartDate and EndDate. For Example the User inputs StartDate - 01/01/2015 and EndDate - 04/01/2015
the output would be like this
Month StartMonth EndMonth TotalSales
1 01/01/2015 01/31/2015 200
2 02/01/2015 02/28/2015 100
3 03/01/2015 03/31/2015 100
4 04/01/2015 04/30/2015 0
Started to something like this
set #Start_act = cast(DATEADD(month, DATEDIFF(month, 0, #StartDate), 0) as date)
set #End_act = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Enddate)+1, 0)) as date)
set #counter = DATEDIFF(month, #Start_act, #End_act)
if(#counter = 1)
begin
set #counter = #counter
end
else
set #counter = #counter + 1
end
set #count = 0
CREATE TABLE #TempTableID
(
Month int,
StartMonth date,
EndMonth date,
TotalSales
)
while (#count <= #counter)
begin
set #count = #count + 1;
if(#count = 1)
begin
set #Start = #Start_act
set #End = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Start_act)+1, 0)) as date)
set #plannedHorseCapacity = 123
end
else
begin
set #Start = cast(DATEADD(d, 1, #End)as date)
set #End = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Start)+1, 0)) as date)
set #plannedHorseCapacity = 456
end
Insert into #TempTableID
(
Month
StartMonth
EndMonth
TotalSales
)
Values
(
#count,
#Start,
#End,
#TotalSales
)
if(#count > #counter)
begin
break
end
else
begin
continue
end
end
Select * from #TempTableID

You can use below script -
WITH cte
AS
(SELECT
CONVERT(VARCHAR(20), MONTH([date]))
+ '-' + CONVERT(VARCHAR(20), YEAR([date])) monthyear
,([sales])
FROM [sales])
--add where condition for from and to date here
SELECT
monthyear
,SUM(sales) totalsales
FROM cte
GROUP BY monthyear
Output will be
monthyear totalsales
1-2015 400
2-2015 50
3-2015 150
Option 2 With Start and End Date
WITH cte
AS
(SELECT
FORMAT(MONTH([date]), '0#')
+ '-' + CONVERT(VARCHAR(20), YEAR([date])) monthyear
,([sales])
FROM [sales])
--add where condition for from and to date here
SELECT
monthyear
,cast (SUBSTRING(monthyear, 1, 2) as int) [Month]
,SUBSTRING(monthyear, 4, 4) [Year]
,(SELECT
DATEADD(MONTH, SUBSTRING(monthyear, 1, 2) - 1, DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0)))
StartDate
,(SELECT
DATEADD(DAY, -1, DATEADD(MONTH, CAST(SUBSTRING(monthyear, 1, 2) AS INT), DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0))))
EndDate
,SUM(sales) totalsales
FROM cte
GROUP BY monthyear
Output will be
monthyear Month Year StartDate EndDate totalsales
01-2015 1 2015 2015-01-01 00:00:00.000 2015-01-31 00:00:00.000 400
02-2015 2 2015 2015-02-01 00:00:00.000 2015-02-28 00:00:00.000 50
03-2015 3 2015 2015-03-01 00:00:00.000 2015-03-31 00:00:00.000 50
11-2015 11 2015 2015-11-01 00:00:00.000 2015-11-30 00:00:00.000 100
EDIT - Sorting
If you have multiple year data, then the dates will not come in sequence to fix that add order by [StartDate] in last.
output will be
monthyear Month Year StartDate EndDate totalsales
01-2015 1 2015 2015-01-01 00:00:00 2015-01-31 00:00:00.000 400
02-2015 2 2015 2015-02-01 00:00:00 2015-02-28 00:00:00.000 50
03-2015 3 2015 2015-03-01 00:00:00 2015-03-31 00:00:00.000 50
11-2015 11 2015 2015-11-01 00:00:00 2015-11-30 00:00:00.000 100
01-2016 1 2016 2016-01-01 00:00:00 2016-01-31 00:00:00.000 125
11-2016 11 2016 2016-11-01 00:00:00 2016-11-30 00:00:00.000 55

You can achieve it like this,
;WITH [CTE_DATE]
AS
(
SELECT MONTH(#fromdt) AS [Month]
,DATEADD(mm, DATEDIFF(mm, 0, #fromdt), 0) AS StartMonth
,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#fromdt)+1,0)) AS EndMonth
UNION ALL
SELECT [Month] + 1 AS [Month]
,DATEADD(MONTH,1,StartMonth) AS StartMonth
,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,DATEADD(MONTH,1,StartMonth))+1,0)) AS EndMonth
FROM [CTE_DATE] WHERE [Month] < MONTH(#todt)
)
SELECT *
,(SELECT SUM(sales) FROM yourtable where [date] between StartMonth and EndMonth) as TotalSales
FROM [CTE_DATE]

You can find end of month as below:
select [Month] = Month([date]), [date] as StartMonth,
EndMonth = convert(date, dateadd(month,datediff(month,0,'2017-02-11')+1,0)-1),
TotalSales from (
select [date], sum(sales) as totalSales
from #yoursales
group by [date]
) a

You could use a CTE like this
DECLARE #SampleData AS TABLE
(
id int,
[date] date,
sales int
)
INSERT INTO #SampleData
VALUES
( 1 , '2015-01-01', 100 ),
( 2 , '2015-01-01', 100 ),
( 3 , '2015-02-01', 100 ),
( 4 , '2015-03-01', 100 )
DECLARE #StartDate date = '2015-01-01'
DECLARE #EndDate date = '2015-08-01'
;WITH temp AS -- calendar table
(
SELECT dateadd(month, datediff(month,0,#StartDate),0) AS [StartMonthDate],
dateadd(day, -1 ,dateadd(month, datediff(month,0,#StartDate) + 1,0)) AS [EndMonthDate]
UNION ALL
SELECT dateadd(month, 1, t.[StartMonthDate]),
dateadd(day,-1,dateadd(month, 2, t.[StartMonthDate]))
FROM temp t
WHERE dateadd(month, 1, t.[StartMonthDate]) <= #EndDate
)
SELECT datepart(year,t.StartMonthDate) AS year,
datepart(month,t.StartMonthDate) AS month,
t.StartMonthDate,
t.EndMonthDate,
coalesce(ap.TotalSales,0) AS TotalSales
FROM temp t
CROSS APPLY
(
SELECT SUM(sales) AS TotalSales
FROM #SampleData sd
WHERE sd.[date] BETWEEN t.StartMonthDate AND t.EndMonthDate
) ap
OPTION (MAXRECURSION 0)
Demo link: http://rextester.com/LWUX67185

;With cte(id,date,sales)
AS
(
SELECT 1,'01/01/2015',100 UNION ALL
SELECT 2,'01/01/2015',100 UNION ALL
SELECT 3,'02/01/2015',100 UNION ALL
SELECT 4,'03/01/2015',100 UNION ALL
SELECT 5,'04/01/2015',NULL
)
SELECT [Id],CONVERT(VARCHAR(10),[DATE], 101) AS [DATE],
CONVERT(VARCHAR(10),[EndMonth],101) AS [EndMonth],
[SumOfSale] From
(
SELECT id,[DATE],EndMonth,SUM(sales) OVER(Partition by [DATE],EndMonth order by EndMonth) AS SumOfSale,
Row_NUmber ()OVER(Partition by [DATE],EndMonth order by id)As Seq From
(
SELECT id, CAST([DATE] AS DATE)[DATE], EOMONTH(date)AS EndMonth,ISNULL(sales,0)As Sales from cte
)Dt
)Final
WHERE Final.Seq=1
OutPut
Month StartMonth EndMonth TotalSales
1 01/01/2015 01/31/2015 200
2 02/01/2015 02/28/2015 100
3 03/01/2015 03/31/2015 100
4 04/01/2015 04/30/2015 0

Related

Calculate quarter for dates given an example end date and quarter number

I have an issue where I need to determine fiscal quarters, but won't always know the start/end dates for the quarters. They will, however, always be 3 months long. What I will know is the ending date of the current quarter, and what quarter and year that refers to. For example, I might be given:
Current Quarter: Q4
Current Year: 2021
Current Quarter End Date: 1/31/2021
How can I get the quarter for any other date? If any of those 3 values were to change, the query still needs to provide the quarter for any given date based on those 3 parameters.
I came up with the following, which puts the last 4 years into a temp table:
DECLARE #QuarterEnd DATE = '1/31/2022'
, #CurrentQuarter INT = 1
, #CurrentYear INT = 2022
, #Counter INT = 16
, #qs INT = 0
, #qe INT = 2
, #DateToTest DATE = '12/15/2021'
CREATE TABLE #Quarters (
StartDate DATE
, EndDate DATE
, Qtr INT
, Yr INT
)
WHILE #Counter <> 0
BEGIN
INSERT INTO #Quarters VALUES (
cast(DATEADD(MONTH, DATEDIFF(MONTH, 0, #QuarterEnd)-#qe , 0) as date)
, cast(DATEADD(MONTH, DATEDIFF(MONTH, -1, #QuarterEnd)-#qs, -1) as date)
, #CurrentQuarter
, #CurrentYear
)
SET #Counter = #Counter - 1
SET #qs = #qs + 3
SET #qe = #qe + 3
SET #CurrentQuarter = CASE WHEN #CurrentQuarter = 1 THEN 4 ELSE #CurrentQuarter - 1 END
SET #CurrentYear = CASE WHEN #CurrentQuarter = 4 THEN #CurrentYear - 1 ELSE #CurrentYear END
END
SELECT #DateToTest
, (SELECT CONCAT('Q', Qtr, ' ', Yr) FROM #Quarters WHERE #DateToTest BETWEEN StartDate and EndDate)
FROM #Quarters
However, this doesn't seem to be practical when I'm running queries that will return hundreds of thousands of records.
I suppose I can throw that into a function and call it with:
SELECT MyQuarter = dbo.MyQuarterFunction(#QuarterEnd, #CurrentQuarter, #CurrentYear, #DateToTest)
There has to be a more efficient way to do this. Any suggestions?
Just create a permanent table called Quarters.
CREATE TABLE dbo.Quarters
(
StartDate date,
QuarterNumber tinyint,
FiscalYear int,
NextQuarterStartDate AS (DATEADD(MONTH, 3, StartDate))
);
INSERT dbo.Quarters(StartDate, QuarterNumber, FiscalYear)
VALUES('20200201',1,2020),
('20200501',2,2020),
('20200801',3,2020),
('20201101',4,2020),
('20210201',1,2021),
('20210501',2,2021),
('20210801',3,2021),
('20211101',4,2021),
('20220201',1,2022),
('20220501',2,2022),
('20220801',3,2022),
('20221101',4,2022);
Now any time you are given a date (like GETDATE()) you can find the other information easily:
DECLARE #date date = GETDATE();
SELECT * FROM dbo.Quarters
WHERE #date >= StartDate
AND #date < NextQuarterStartDate;
Example db<>fiddle
If you need to support multiple fiscal calendars simultaneously, just add a column (like CalendarID or CompanyID or CustomerID).
And really, you don't even need a calendar or quarters table for this. You already have a table of clients, right? Just add a column to store what month their fiscal year starts. That's really all you need.
CREATE TABLE dbo.Clients
(
ClientID int NOT NULL CONSTRAINT PK_Clients PRIMARY KEY,
Name nvarchar(200) NOT NULL CONSTRAINT UQ_ClientName UNIQUE,
FiscalYearStart tinyint NOT NULL CONSTRAINT CK_ValidMonth
CHECK (FiscalYearStart BETWEEN 1 AND 12)
);
Now let's insert a few rows with some clients with different fiscal years:
INSERT dbo.Clients(ClientID, Name, FiscalYearStart)
VALUES(1, N'ClientFeb', 2), -- fiscal year starts in February
(2, N'ClientMay', 5), -- fiscal year starts in May
(3, N'ClientNormal', 1); -- fiscal year matches calendar
Now, yes, we need a function, but let's not do any while loops or counters or #temp tables.
CREATE FUNCTION dbo.GetLast16Quarters
(
#DateToTest date,
#ClientID int
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
(
WITH n(n) AS
(
SELECT n = 1 UNION ALL
SELECT n + 1 FROM n WHERE n < 20
),
Last20Quarters(QuarterStart, FiscalYearStart) AS
(
SELECT QuarterStart = DATEADD(QUARTER, 1-n,
DATEFROMPARTS(YEAR(#DateToTest)+1, FiscalYearStart, 1)),
FiscalYearStart
FROM dbo.Clients CROSS JOIN n WHERE ClientID = #ClientID
),
Last16Quarters AS
(
SELECT TOP (16) QuarterStart,
y = YEAR(DATEADD(MONTH, 1-FiscalYearStart, QuarterStart))
FROM Last20Quarters WHERE QuarterStart < #DateToTest
ORDER BY QuarterStart DESC
)
SELECT QuarterStart,
QuarterEnd = EOMONTH(QuarterStart, 2),
FiscalYear = y,
QuarterNumber = ROW_NUMBER() OVER
(PARTITION BY y ORDER BY QuarterStart)
FROM Last16Quarters);
Then to call it:
DECLARE #DateToTest date = '20211215';
SELECT * FROM dbo.GetLast16Quarters(#DateToTest, 1);
Output:
QuarterStart
QuarterEnd
FiscalYear
QuarterNumber
2018-02-01
2018-04-30
2018
1
2018-05-01
2018-07-31
2018
2
2018-08-01
2018-10-31
2018
3
2018-11-01
2019-01-31
2018
4
2019-02-01
2019-04-30
2019
1
2019-05-01
2019-07-31
2019
2
2019-08-01
2019-10-31
2019
3
2019-11-01
2020-01-31
2019
4
2020-02-01
2020-04-30
2020
1
2020-05-01
2020-07-31
2020
2
2020-08-01
2020-10-31
2020
3
2020-11-01
2021-01-31
2020
4
2021-02-01
2021-04-30
2021
1
2021-05-01
2021-07-31
2021
2
2021-08-01
2021-10-31
2021
3
2021-11-01
2022-01-31
2021
4
Example db<>fiddle
Assuming that you have two input variables:
declare #quarter_end date = '2021-01-31';
declare #current_quarter int = 4;
You can calculate the first month of financial year:
declare #first_month_of_fy int = (month(#quarter_end) - #current_quarter * 3 + 12) % 12 + 1;
-- 2 i.e. February
And use that value to calculate the quarter and year for any date using some math:
select *
from (values
('2020-12-15'),
('2021-01-15'),
('2021-12-15'),
('2022-01-15')
) as t(testdate)
cross apply (select
(month(testdate) - #first_month_of_fy + 12) % 12 + 1
) as ca1(month_of_fy)
cross apply (select
(month_of_fy - 1) / 3 + 1,
year(dateadd(month, 12 - month_of_fy, dateadd(day, - day(testdate) + 1, testdate)))
) as ca2(fy_quarter, fy_year)
DB<>Fiddle
I ended up creating a function to handle this. Since I'm given the last day of the quarter, which quarter it is, and which year it is, I can determine the start and end date of that fiscal year. Since a quarter is always 3 months, I can also determine which months fall into which quarter.
The first 4 variables, #qa, #qb, #qc, #qd hold a comma separated list of the months within each quarter (#qa is current quarter, #qb is current quarter -1, #qc is current quarter -2, and #qd is current quarter -3)
The second 2 variables determine the first day and last day of the fiscal calendar
To get the quarter & year, I first get the month from the supplied date (#Date) and see if it's in #qa, #qb, #qc or #qd. That tells me the fiscal quarter.
Finally, I compare the given date to the start and end date of the current fiscal year, and to the 6 years prior (going back 6 years is enough for what I need)
CREATE FUNCTION [dbo].[FunctionNameHere]
(
#Date DATE
, #QuarterEnd DATE
, #CurrentQuarter INT
, #CurrentYear INT
)
RETURNS VARCHAR(7)
AS
BEGIN
DECLARE #qa VARCHAR(8) = (concat(datepart(m, dateadd(m, 0, #QuarterEnd)),',', datepart(m, dateadd(m, -1, #QuarterEnd)),',', datepart(m, dateadd(m, -2, #QuarterEnd))))
DECLARE #qb VARCHAR(8) = (concat(datepart(m, dateadd(m, -3, #QuarterEnd)),',', datepart(m, dateadd(m, -4, #QuarterEnd)),',', datepart(m, dateadd(m, -5, #QuarterEnd))))
DECLARE #qc VARCHAR(8) = (concat(datepart(m, dateadd(m, -6, #QuarterEnd)),',', datepart(m, dateadd(m, -7, #QuarterEnd)),',', datepart(m, dateadd(m, -8, #QuarterEnd))))
DECLARE #qd VARCHAR(8) = (concat(datepart(m, dateadd(m, -9, #QuarterEnd)),',', datepart(m, dateadd(m, -10, #QuarterEnd)),',', datepart(m, dateadd(m, -11, #QuarterEnd))))
DECLARE #YearStart DATE = DATEADD(d, 1, DATEADD(q, -#CurrentQuarter, #QuarterEnd))
DECLARE #YearEnd DATE = DATEADD(q, 4-#CurrentQuarter, #QuarterEnd)
DECLARE #Qtr VARCHAR(8) = CONCAT('Q', CASE WHEN DATEPART(m, #Date) IN (SELECT value FROM string_split(#qa, ',')) THEN #CurrentQuarter
WHEN DATEPART(m, #Date) IN (SELECT value FROM string_split(#qb, ',')) THEN CASE WHEN #CurrentQuarter = 1 THEN 4
WHEN #CurrentQuarter = 2 THEN 1
WHEN #CurrentQuarter = 3 THEN 2
WHEN #CurrentQuarter = 4 THEN 3 END
WHEN DATEPART(m, #Date) IN (SELECT value FROM string_split(#qc, ',')) THEN CASE WHEN #CurrentQuarter = 1 THEN 3
WHEN #CurrentQuarter = 2 THEN 4
WHEN #CurrentQuarter = 3 THEN 1
WHEN #CurrentQuarter = 4 THEN 2 END
WHEN DATEPART(m, #Date) IN (SELECT value FROM string_split(#qd, ',')) THEN CASE WHEN #CurrentQuarter = 1 THEN 2
WHEN #CurrentQuarter = 2 THEN 3
WHEN #CurrentQuarter = 3 THEN 4
WHEN #CurrentQuarter = 4 THEN 1 END
END,
' ',
CASE WHEN #Date BETWEEN #YearStart AND #YearEnd THEN #CurrentYear
WHEN #Date BETWEEN dateadd(Year, -1, #YearStart) AND dateadd(Year, -1, #YearEnd) THEN #CurrentYear - 1
WHEN #Date BETWEEN dateadd(Year, -2, #YearStart) AND dateadd(Year, -2, #YearEnd) THEN #CurrentYear - 2
WHEN #Date BETWEEN dateadd(Year, -3, #YearStart) AND dateadd(Year, -3, #YearEnd) THEN #CurrentYear - 3
WHEN #Date BETWEEN dateadd(Year, -4, #YearStart) AND dateadd(Year, -4, #YearEnd) THEN #CurrentYear - 4
WHEN #Date BETWEEN dateadd(Year, -5, #YearStart) AND dateadd(Year, -5, #YearEnd) THEN #CurrentYear - 5
WHEN #Date BETWEEN dateadd(Year, -6, #YearStart) AND dateadd(Year, -6, #YearEnd) THEN #CurrentYear - 6
ELSE 9999 END)
return #Qtr
END

How to get a month prior date from a given date in SQL?

I am trying to get a month dates from the #EndDate including provided date (#EndDate) in SQ Server 2008.
#NoOfMonths is a variable which decides how much previous months dates we need.
e.g.
#EndDate = 2020-07-28
#NoOfMonths = 6
Expected result would be:
2020-07-28
2020-06-28
2020-05-28
2020-04-28
2020-03-28
2020-02-28
I am trying using below recursive CTE query, however the results are not expected, I am getting month end dates.
#EndDate: 2020-07-28
#NoOfMonths = 6
Result:
2020-07-31
2020-06-30
2020-05-31
2020-04-30
2020-03-31
2020-02-29
Code:
DECLARE #EndDate DATE = CAST('2020 - 07 - 28' AS DATE);
DECLARE #NoOfMonths INT = 6;
WITH CTE_previousMonths AS
(
SELECT
CAST(DATEADD(ss, -1, DATEADD(mm, DATEDIFF(m, -1, #EndDate), 0)) AS DATE) AS MonthPriorDate,
1 AS months
UNION ALL
SELECT
CAST(DATEADD(ss, -1, DATEADD(mm, DATEDIFF(m, 0, MonthPriorDate), 0)) AS DATE) AS MonthPriorDate,
months + 1 AS months
FROM
CTE_previousMonths
WHERE
months < #NoOfMonths
)
SELECT CTE_previousMonths.MonthPriorDate
FROM CTE_previousMonths;
Thanks!
I think this should do what you want:
with n as (
select 1 as n
union all
select n + 1
from n
where n < #NoOfMonths
)
select dateadd(month, 1 - n, #enddate)
from n;
Using Eomonth function:
WITH cte1 as
(
select EOMONTH('2020-07-28') as last_date, DATEADD(MONTH, -5, EOMONTH('2020-07-28')) AS END_DATE--Number of months - 1
union all
select DATEADD(MONTH, -1, last_date), END_DATE FROM CTE1 WHERE LAST_DATE > END_DATE
)
SELECT last_date FROM cte1;

calculate start date and end date from given quarter SQL

I want to get :
startdate and enddate from a given quarter from between dates
example :
range of dates : 2016-01-01 - 2016-12-31
1 (quarter) - will give me :
start date
2016-01-01
enddate
2016-03-31
2 (quarter) - will give me :
start date
2016-04-01
enddate
2016-06-30
and so on
I made it for only Quarter name and Year, modified it as your need
-- You may need to extend the range of the virtual tally table.
SELECT [QuarterName] = 'Q' + DATENAME(qq,DATEADD(QQ,n,startdate)) + ' ' + CAST(YEAR(DATEADD(QQ,n,startdate)) AS VARCHAR(4))
FROM (SELECT startdate = '01/Jan/2016', enddate = '31/DEC/2016') d
CROSS APPLY (
SELECT TOP(1+DATEDIFF(QQ,startdate,enddate)) n
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) rc(n)
) x
Check below logic to get your answer.
DECLARE #Year DATE = convert(varchar(20),datepart(YEAR,getdate()))+'-01'+'-01'
DECLARE #Quarter INT = 4
SELECT DATEADD(QUARTER, #Quarter - 1, #Year) ,
DATEADD(DAY, -1, DATEADD(QUARTER, #Quarter, #Year))
SELECT DATEADD(QUARTER, d.q, DATEADD(YEAR, DATEDIFF(YEAR, 0,GETDATE()), 0))
AS FromDate,
DATEADD(QUARTER, d.q + 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), -1))
AS ToDate
FROM (
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3
) AS d(q)

SQL Query to Count number of values in a column Per Month Conditionally

id date sales req arivve
1 01/01/2015 100 accept deny
2 01/01/2015 100 deny deny
3 02/01/2015 100 accept accept
4 03/01/2015 100 accept deny
What I need is to count the number of 'accept' value in both req and arrive column per month range. This is the expected output
Month StartMonth EndMonth TotalSales reqCount arriveAcount
1 01/01/2015 01/31/2015 200 1 0
2 02/01/2015 02/28/2015 100 1 1
3 03/01/2015 03/31/2015 100 1 0
4 04/01/2015 04/30/2015 0 0 0
This is what I have already (input of start and end year is provided by user so the month tange is dynamic)
--step 1
select id, date, capacity, sales, req, arrive
into #First
from Sales
where date between #StartDate and #EndDate;
--step 2 b
WITH cte
AS
(SELECT
FORMAT(MONTH([schedule_date]), '0#')
+ '-' + CONVERT(VARCHAR(20), YEAR([schedule_date])) monthyear
,([capacity]), request_status, arrival_status
FROM #First)
SELECT
(SELECT DATEADD(MONTH, SUBSTRING(monthyear, 1, 2) - 1, DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0))) StartDate
,(SELECT DATEADD(DAY, -1, DATEADD(MONTH, CAST(SUBSTRING(monthyear, 1, 2) AS INT), DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0)))) EndDate
,SUM(capacity) totalsales
FROM cte
GROUP BY monthyear
Try:
select DATEPART(month, date)
, date as StartMonth
, dateadd(day, -1 , DATEADD(month, 1, date))
, sum(sales)
, sum(case when req = 'accept' then 1 else 0 end) reqCount
, sum(case when arivve = 'accept' then 1 else 0 end) arriveCount
from mytable
group by date

Generate list of dates based on the day and occurrence of the month

I want to generate based on the day of the week and number of the occurrence in the month of a date, a list of dates for each month between two dates. Assuming I have a #StartDate = 2016/04/01 and #EndDate = 2016/09/01, i check that #StartDate is on a first Friday of April, then to #EndDate will create dates for all first Friday of each month:
2016/05/06
2016/06/03
2016/07/01
2016/08/05
In case #StartDate = 2016/04/12 and #EndDate = 2016/09/01, I note that the #StartDate is the second Tuesday of April, then went to get every second Tuesday Tuesday of each month :
2016/05/10
2016/06/14
2016/07/12
2016/08/09
In case#StartDate = 2016/04/28 and #EndDate = 2016/09/01, I note that the #StartDate is on the last Thursday of the month of April:
2016/05/26
2016/06/30
2016/07/28
2016/08/25
In the last case, i need to verify the number of weeks of each month, because exists months only with 4 weeks or with 5 weeks and i want the last occurrence.
What I have done? I found a code that gives me every Monday in the third week of the month, and i adopted a little to get a #StartDate and #EndDate:
;with
filler as (select row_number() over (order by a) a from (select top 100 1 as a from syscolumns) a cross join (select top 100 1 as b from syscolumns) b),
dates as (select dateadd(month, a-1, #StartDate ) date from filler where a <= 1000 and dateadd(month, a-1, #StartDate) < #EndDate),
FirstMonday as (
select dateadd(day, case datepart(weekday,Date) /*this is the case where verify the week day*/
when 1 then 1
when 2 then 0
when 3 then 6
when 4 then 5
when 5 then 4
when 6 then 3
when 7 then 2
end, Date) as Date
,case when datepart(weekday, #StartDate) = 1 then 3 else 2 end as Weeks /*here i verify the number of weeks to sum in the next date*/
from dates
)
select dateadd(week, Weeks, Date) as ThirdMonday
from FirstMonday
So, it is:
set #NumSemana = datepart(day, datediff(day, DATEADD(mm, DATEDIFF(mm,0,#StartDate), 0), #StartDate)/7 * 7)/7 + 1;
WITH AllDays
AS ( SELECT #StartDate AS [Date], DATEPART(month, #StartDate) as validMonth
UNION ALL
SELECT DATEADD(week, 1, [Date]),
iif(DATEPART(month,DATEADD(week, 1, [Date])) < validMonth + #PeriodicityRepeat, validMonth, validMonth + #PeriodicityRepeat)
FROM AllDays
WHERE
DATEPART(month,[Date]) <= DATEPART(month,#EndDate)
and DATEPART(year,[Date]) <= DATEPART(year,#EndDate)
),
rankedDays
AS(
SELECT [Date], validMonth,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date]) ascOrder,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date] desc) descOrder
FROM AllDays
WHERE DATEPART(month, [Date]) = validMonth
)
select [Date]
from rankedDays
where ((ascOrder = #NumSemana and #NumSemana <=4 )
or (descOrder = 1 and #NumSemana = 5)
or [Date] = #StartDate )
and [Date] < #EndDate
OPTION (MAXRECURSION 0)
Query:
DECLARE #StartDate DATE = '2016-04-28',
#EndDate DATE = '2016-09-01'
;WITH dates AS (
SELECT DATEADD(week, -5, #StartDate) as date_
UNION ALL
SELECT DATEADD(week,1,date_)
FROM dates
WHERE DATEADD(week,1,date_) < #enddate
), final AS (
SELECT ROW_NUMBER() OVER (PARTITION BY DATEPART(year,date_), DATEPART(month,date_) ORDER BY date_ ASC) as RN,
date_
FROM dates
), weeks AS (
SELECT *
FROM (VALUES
(1,1),
(2,2),
(3,3),
(4,4),
(4,5),
(5,4),
(5,5)
) as t(w1,w2)
WHERE w1 = (SELECT RN FROM final WHERE date_ = #StartDate)
)
SELECT MAX(date_) as date_
FROM final f
INNER JOIN weeks w ON f.RN = w.w2
WHERE date_ between #StartDate and #EndDate AND date_ != #StartDate
GROUP BY DATEPART(YEAR,date_), DATEPART(MONTH,date_)
ORDER BY MAX(date_) ASC
Outputs:
For #StartDate = 2016/04/01 and #EndDate = 2016/09/01
date_
----------
2016-05-06
2016-06-03
2016-07-01
2016-08-05
(4 row(s) affected)
For #StartDate = 2016/04/12 and #EndDate = 2016/09/01
date_
----------
2016-05-10
2016-06-14
2016-07-12
2016-08-09
(4 row(s) affected)
For #StartDate = 2016/04/28 and #EndDate = 2016/09/01
date_
----------
2016-05-26
2016-06-30
2016-07-28
2016-08-25
(4 row(s) affected)