Combine Queries where one is recursive - sql

I have a recursive query that creates a row for each hour of the previous month as follows;
WITH a AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM a
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE(), -1)
)
SELECT
a.*
FROM a
OPTION (maxrecursion 0);
This produces these results;
DateTime
2020-11-01 00:00:00.000
2020-11-01 01:00:00.000
2020-11-01 02:00:00.000
etc.....
Next I have a query that calculates the number of calls per customer, per hour, for the previous month, as a 4 week average as follows;
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*)/4 AS Average
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
This produces the following results;
RoundedToHour Campaign Average
2020-11-01 02:00:00.000 Client1 0
2020-11-01 04:00:00.000 Client2 2
etc....
What I am having trouble doing is combining these two. My initial thoughts were to use a CTE of the recursive query as basically a where clause in my second query, but since you have to use a WITH for CTE's, and I have to use a WITH for my recursive query, that won't directly work, because you can't have nested WITH's.
My final result I am looking for is a single query that produces the 4 week average of calls for each hour of the previous month, for each client. I am open to changing how I am doing any of this if someone has a better suggestion on how to reach my ultimate goal.

You can't declare a CTE in a sub-query but you can declare multiple CTEs together. So declare both and then LEFT JOIN them.
WITH a AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM a
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE(), -1)
), b AS (
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*)/4 AS Average
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
)
SELECT a.*, b.*
FROM a
LEFT JOIN b on b.RoundedToHour = a.DateTime
OPTION (maxrecursion 0);

Related

Create table with each customer and each hour of the month

I have a list of customers that we can receive calls for each month. I generate a list with a simple
SELECT DISTINCT(Campaign)
FROM [Reporting].[dbo].[New_Five9_CallLog]
WHERE DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
I also have short little recursion query I can use to create an entry for each hour of the current month
WITH time_CTE
AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE())
)
SELECT *
FROM time_CTE
OPTION (maxrecursion 0);
I also have a query that will show me the total calls received for each client, for each hour of the month
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*) AS Actual
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
My ultimate goal is to have a procedure I can run at the beginning of each month that will update a table and add a 4 week average for each client, for each hour of the month. I have the 3 basic parts above I believe, but for the life of me I can't figure out how to add it all together properly.
My first attempt was to create a table that simply had a row for each hour of the month, for each client that we can get calls for.
DECLARE #date DateTime SELECT DATEADD(DAY, 1, EOMONTH(getdate(), -1))
DECLARE #comp varchar SELECT DISTINCT(Campaign) FROM [Reporting].[dbo].[New_Five9_CallLog] WHERE
DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
WHILE #date < EOMONTH(GETDATE())
BEGIN
WITH time_CTE ([DateTime],Comp)
AS
(
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #date), 0) AS [DateTime],
#comp
UNION ALL
SELECT dateadd(hour, 1, [DateTime]),
#comp
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(#date)
)
SELECT *
FROM time_CTE
OPTION (maxrecursion 0);
SET #date = #date +1;
END;
However this did practically nothing. I understand SQL pretty well, but I have never had to do a For Loop in SQL before, and I just can't wrap my head around how to structure this to make it work.
The end table would hopefully look like this;
DateTime Campaign Avg
1/1/2021 00:00 Client 1 4
1/1/2021 00:00 Client 2 0
1/1/2021 01:00 Client 1 2
1/1/2021 01:00 Client 2 1
etc....
I was able to make this work with the following, which will create a row for each hour of the month, for each client that has calls.
DECLARE #CompTable TABLE (comp varchar(max))
INSERT INTO #CompTable
SELECT DISTINCT Campaign
FROM [Reporting].[dbo].[New_Five9_CallLog]
WHERE DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
DECLARE #comp varchar(max)
WHILE EXISTS (SELECT 1 FROM #CompTable)
BEGIN
SELECT TOP 1 #comp = comp FROM #CompTable;
WITH Time_CTE ([DateTime])
AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE())
)
SELECT *,
(Select TOP 1 #comp
FROM #CompTable) AS Campaign
FROM time_CTE
OPTION (maxrecursion 0)
DELETE FROM #CompTable WHERE comp = #comp
END

How to determine the number of days in a month for a given Date Range?

I need to calculate using SQL Query, how many days within a given range fall into each calendar month.
I have given 2 dates, which define a date range; for example 2020-01-01 to 2020-08-03. I need to find how many days in that range fall in to each month i.e. how many fall into July, and how many into August.
In the example given, the expected result is 31 days in July and 3 days in August.
One approach uses a recusive query. Using date artithmetics, we can build the query so it performs one iteration per month rather than one per day, so this should be a rather efficient approach:
with cte as (
select
datefromparts(year(#dt_start), month(#dt_start), 1) month_start,
1 - day(#dt_start) + day(
case when #dt_end > eomonth(#dt_start)
then eomonth(#dt_start)
else #dt_end
end
) as no_days
union all
select
dateadd(month, 1, month_start),
case when #dt_end > dateadd(month, 2, month_start)
then day(eomonth(dateadd(month, 1, month_start)))
else day(#dt_end)
end
from cte
where dateadd(month, 1, month_start) <= #dt_end
)
select * from cte
Demo on DB Fiddle.
If we set the boundaries as follows:
declare #dt_start date = '2020-07-10';
declare #dt_end date = '2020-09-10';
Then the query returns:
month_start | no_days
:---------- | ------:
2020-07-01 | 22
2020-08-01 | 31
2020-09-01 | 10
You can refer this
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
where d.thedate between '2020-01-01' and '2020-08-03'
group by datediff(m, 0, d.thedate)
order by themonth;
Please refer the link below where RichardTheKiwi user given a clear example for your scenario.
SQL Server query for total number of days for a month between date ranges
You can do all the work at the month level rather than the day level -- which should be a bit faster. Here is a method using a recursive CTE:
with cte as (
select #startdate as startdate, #enddate as enddate,
datefromparts(year(#startdate), month(#startdate), 1) as month
union all
select startdate, enddate, dateadd(month, 1, month)
from cte
where dateadd(month, 1, month) < #enddate
)
select month,
(case when month <= startdate and dateadd(month, 1, month) >= enddate
then day(enddate) - day(startdate) + 1
when month <= startdate
then day(eomonth(month)) - day(startdate) + 1
when dateadd(month, 1, month) < enddate
then day(eomonth(month))
when dateadd(month, 1, month) >= enddate
then day(enddate)
end)
from cte;
And the db<>fiddle.
The logic is simpler at the day level:
with cte as (
select #startdate as dte, #enddate as enddate
union all
select dateadd(day, 1, dte), enddate
from cte
where dte < enddate
)
select datefromparts(year(dte), month(dte), 1) as yyyymm, count(*)
from cte
group by datefromparts(year(dte), month(dte), 1)
order by yyyymm
option (maxrecursion 0)
Here is a solution with recursive CTE.
declare #startDate date = '2020-07-01'
declare #endDate date = '2020-08-03'
; WITH cte (n, year, month, daycnt)
AS (
SELECT
0
, DATEPART(year, #startDate)
, DATENAME(MONTH, #startDate)
, DATEPART(day, EOMONTH( #startDate ) ) - DATEPART(day, #startDate ) + 1
UNION ALL
SELECT
n + 1
, DATEPART(year, DATEADD(month, n + 1, #startDate) )
, DATENAME(MONTH, DATEADD(month, n + 1, #startDate) )
, IIF(
n = ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
, DATEPART(day, #endDate )
, DATEPART(day, EOMONTH( DATEADD(month, n + 1, #startDate) ) )
)
FROM
cte
WHERE
n <= ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
)
SELECT *
FROM cte
ORDER BY n
OPTION (maxrecursion 0)
This could be further simplified with a number function but that would also be essentially be a recursive CTE, though it would definitely look cleaner. But it requires defining a function on top of this SELECT statement.

How to get all dates between current month and the two last months

I am trying to get all dates existing between the current month and the two last months.
For example: today 10-01-2019
With an sql script, I will get all dates between 2018-10-01 and 2019-01-31.
with cte as
(
select getdate() as n
union all
select dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
union all
select dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
)
select * from cte
I get
error Msg 530, Level 16, State 1, Line 1
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Recursion is not a good approach to this. Performance wise using a recursive cte to increment a counter is the same thing as a cursor. http://www.sqlservercentral.com/articles/T-SQL/74118/
A much better approach is to do this set based. For this task a tally table is ideal. Here is a great article on the topic.
I keep a tally table as a view in my system. It is lightning fast with zero reads.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Then for something like this problem it is super simple to use. This will produce the same results with no looping.
declare #endDate datetime = '2019-01-31'
, #tmpDate datetime = '2018-10-01'
select dateadd(day, t.N - 1, #tmpDate)
from cteTally t
where t.N - 1 <= DATEDIFF(day, #tmpDate, #endDate)
--EDIT--
If you need this to be dynamic you can use a little date math. This will get the data from the beginning of 3 months ago through the end of the current month regardless of when you run this. The date logic might be a little tough to decipher if you haven't seen this kind of thing before. Lynn Pettis has a great article on this topic. http://www.sqlservercentral.com/blogs/lynnpettis/2009/03/25/some-common-date-routines/
select dateadd(day, t.N - 1, dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)))
from cteTally t
where t.N - 1 < datediff(day,dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)), dateadd(month, datediff(month, 0, getdate()) + 1, 0))
This will work depending on your version of SQL Server.
with cte as
(
select cast(getdate() as date) as n
union all
select dateadd(DAY,-1,n) from cte where dateadd(DAY,-1,n) > (select eomonth(cast(dateadd(month,-4,getdate()) as date)))
)
select *
from cte
order by n desc
option (maxrecursion 200)
with cte as
(
select dateadd(month,1,dateadd(day, -1* day(getdate()) , cast(getdate() as date) ) ) n
union all
select dateadd(day,-1,n) from cte where month(n) + year(n) * 12 >= month(getdate()) + year(getdate()) * 12 -3
),
final as (select * from cte except select top 1 * from cte order by n)
select * from final order by n
OPTION (MAXRECURSION 1000)
or to use dateadd only and avoid the except
with cte as
(
select dateadd(day,-1,dateadd(month,1,dateadd(day, 1 - day(getdate()) , cast(getdate() as date)))) n
union all
select dateadd(day,-1,n) from cte where n > dateadd(month,-3,dateadd(day , 1 - day(getdate()),cast(getdate() as date)))
)
select * from cte order by n
OPTION (MAXRECURSION 1000)
You're hitting the maxrecursion limit. Increase it as an option:
with cte as
(
select getdate() as n
union all
select dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
union all
select dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
)
select * from cte
OPTION (MAXRECURSION 1000)
You can use a temp table for this purpose. With a loop, just add the dates you need to the temp table. Check the query below:
create table #temp (thedate date)
declare #i int = 1
declare #tmpDate datetime = dateadd(month,-2,getdate())
while #tmpDate<=getdate()
begin
insert into #temp
values (#tmpDate)
set #tmpDate = dateadd(day,1,#tmpDate)
end
select * from #temp
EDIT: Based on OP's comment, new query:
create table #temp (thedate date)
declare #i int = 1
declare #endDate datetime = '2019-01-31'
declare #tmpDate datetime = '2018-10-01'
while #tmpDate<=#endDate
begin
insert into #temp
values (#tmpDate)
set #tmpDate = dateadd(day,1,#tmpDate)
end
select * from #temp
If you're using SQL 2012+
SELECT
dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate()))))) as TheDate
FROM
master..spt_values m1
WHERE
type = 'P'
AND dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate())))) ) <= eomonth(getdate())
And for earlier versions of SQL:
SELECT
cast(dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) as date)
FROM
master..spt_values m1
WHERE
type = 'P'
AND dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) <= dateadd(MM, 1, dateadd(dd, -day(getdate()) , getdate()))

SQL Server grouped rows return with default values if no row available for a date period

I'm trying to write a stored procedure which groups up rows based on their month and return a sum of all items if they exist and 0 if they don't.
For the date part of the query, what I am trying to get is today's date - extract the month and go back 5 months to gather any data if it exists.
At this stage, the query runs fine as is but I'm wondering if there's any way to optimise this as it looks like I'm running the same set of data over and over again and also it's hard coded to an extent.
The dataset I am trying to achieve is as follows:
Month TotalAmount TotalCount
-----------------------------------
2017-11 0 0
2017-12 200.00 2
2018-01 300.00 3
2018-02 0 0
2018-03 300.00 3
2018-04 100.00 1
Using the following query below, I was able to achieve what I want but as you can see, it's hard coding back the past 5 months so if I wanted to go back 12 months, I'd have to add in more code.
DECLARE #5MonthAgo date = CAST(DATEADD(MONTH, -5, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -5, GETDATE())) AS DATE)
DECLARE #4MonthAgo date = CAST(DATEADD(MONTH, -4, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -4, GETDATE())) AS DATE)
DECLARE #3MonthAgo date = CAST(DATEADD(MONTH, -3, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -3, GETDATE())) AS DATE)
DECLARE #2MonthAgo date = CAST(DATEADD(MONTH, -2, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -2, GETDATE())) AS DATE)
DECLARE #1MonthAgo date = CAST(DATEADD(MONTH, -1, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -1, GETDATE())) AS DATE)
DECLARE #CurrentMonth date = CAST(GETDATE() + 1 - DATEPART(DAY, GETDATE()) AS DATE)
-- Table to return grouped and sum data
DECLARE #StatsTable TABLE ([Month] DATE,
[Total Amount] DECIMAL(18,2),
[Total Count] INT
)
-- Temporary table to hold onto data batch - so table isn't used later on
DECLARE #TempGenTable TABLE ([Id] INT,
[Date] DATETIME,
[Lines] INT NULL,
[Amount] DECIMAL(18, 2) NULL
)
INSERT INTO #TempGenTable
SELECT
Id, Date, Lines, Amount
FROM
TallyTable
WHERE
Date >= #5MonthAgo
INSERT INTO #StatsTable
SELECT
#5MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
UNION
SELECT
#4MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
...
Is there an easier way to be able to get the above data with more flexibility in the number of months?
Is it better to just have the query pass in a month variable and it checks just the current month and have a loop within the controller to go back x number of months?
I would generate the data using a recursive CTE and then use left join:
with months as (
select datefromparts(year(getdate()), month(getdate()), 1) as month_start, 5 as n
union all
select dateadd(month, -1, month_start), n - 1
from months
where n > 0
)
select m.month_start, count(s.id), sum(s.amount)
from months m left join
#StatsTable s
on m.month_start = s.month
group by m.month_start
order by m.month_start;
You haven't provided sample data, so I'm not sure what s.month looks like. You might want the join condition to be:
on s.month >= m.month_start and s.month < dateadd(month, 1, m.month_start)
Below is a set-based method to generate the needed monthly periods:
--sample data
CREATE TABLE dbo.TallyTable (
Id int
, Date datetime
, Lines int
, Amount decimal(18, 2)
);
INSERT INTO dbo.TallyTable
VALUES
(1, '2017-12-05', 1, 50.00)
,(2, '2017-12-06', 1, 150.00)
,(3, '2018-01-10', 1, 100.00)
,(4, '2018-01-11', 1, 100.00)
,(5, '2018-01-12', 1, 100.00)
,(6, '2018-03-15', 1, 225.00)
,(7, '2018-03-15', 1, 25.00)
,(8, '2018-03-15', 1, 50.00)
,(9, '2018-04-20', 1, 100.00);
GO
DECLARE #Months int = 5; --number of historical months
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t100 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t10 AS a CROSS JOIN t10 AS b)
, periods AS (SELECT
CONVERT(varchar(7), DATEADD(month, DATEDIFF(month, '', GETDATE()) - num, ''),121) AS Month
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num, '') AS PeriodStart
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num + 1, '') AS NextPeriodStart
FROM t100
WHERE num <= #Months
)
SELECT periods.Month, COALESCE(SUM(Amount), 0) AS TotalAmount, COALESCE(COUNT(ID), 0) AS TotalCount
FROM periods
LEFT JOIN dbo.TallyTable ON
TallyTable.Date >= PeriodStart
AND TallyTable.Date < NextPeriodStart
GROUP BY periods.Month
ORDER BY periods.Month;

Get first day and month for last x calendar weeks

I want to get last 8 week starting from today ( GETDATE() )
So the format must be dd/mm for all 8 weeks.
I tried something like this
select "start_of_week" = cast(datepart(dd,dateadd(week, datediff(week, 0, getdate()), 0)) as CHAR(2))+'/'+cast(datepart(mm,dateadd(week, datediff(week, 0, getdate()), 0)) as CHAR(2));
and this is good only for the current week, but how to put this in query and return for curr-1,curr-2,...curr-7 weeks. The final result must be table with some amounts for one player and each week in format dd/mm
Maybe as easy as this?
WITH EightNumbers(Nmbr) AS
(
SELECT 0
UNION SELECT -1
UNION SELECT -2
UNION SELECT -3
UNION SELECT -4
UNION SELECT -5
UNION SELECT -6
UNION SELECT -7
UNION SELECT -8
)
SELECT CONVERT(VARCHAR(5),GETDATE()+(Nmbr*7),103)
FROM EightNumbers
ORDER BY Nmbr DESC
If you need (as the title suggests) the "first day" of the week, you might change the select to:
SELECT CONVERT(VARCHAR(5),GETDATE()+(Nmbr*7)-DATEPART(dw,GETDATE())+##DATEFIRST,103)
FROM EightNumbers
ORDER BY Nmbr DESC
Be aware that the "first day of week" depends on your system's culture. Have a look on ##DATEFIRST!
The result:
28/12
21/12
14/12
07/12
30/11
23/11
16/11
09/11
02/11
Here you go:
DECLARE #DateTable TABLE ( ADate DATETIME )
DECLARE #CurrentDate DATETIME
SET #CurrentDate = GETDATE()
WHILE (SELECT COUNT(*) FROM #DateTable WHERE DATEPART( dw, ADate ) = 2) <= 7
BEGIN
INSERT INTO #DateTable
SELECT #CurrentDate
SET #CurrentDate = DATEADD( dd, -1, #CurrentDate )
END
SELECT "start_of_week" = cast(datepart(dd,dateadd(week, datediff(week, 0, ADate), 0)) as CHAR(2))
+'/'+cast(datepart(mm,dateadd(week, datediff(week, 0, ADate), 0)) as CHAR(2))
FROM #DateTable
WHERE DATEPART( dw, ADate ) = 2
DELETE #DateTable
OUTPUT
start_of_week
28/12
21/12
14/12
7 /12
30/11
23/11
16/11
9 /11
Syntax :
select "start_of_week" =
cast(datepart(dd,dateadd(week, datediff(week, 0, getdate()) - X, 0)) as CHAR(2))
select "start_of_week" =
cast(datepart(dd,dateadd(week, datediff(week, 0, getdate()) - 0, 0)) as CHAR(2)) ,
"previous_week1" =
+'/'+cast(datepart(mm,dateadd(week, datediff(week, 0, getdate()) - 1, 0)) as CHAR(2)),
"previous_week2" =
+'/'+cast(datepart(mm,dateadd(week, datediff(week, 0, getdate()) - 2, 0)) as CHAR(2)),
"previous_week3" =
+'/'+cast(datepart(mm,dateadd(week, datediff(week, 0, getdate()) - 3, 0)) as CHAR(2));
and so on.... thank you
Assuming sys.all_objects has at least 8 rows and you want the first day of the week (which you did not specify in your question:
select top 8 convert(varchar(5),
dateadd(WEEK,
1-1* ROW_NUMBER() over(order by newid()),
dateadd(DD,
1-datepart(dw,getdate()),
getdate())),
1) as [FirstDayOfWeek]
from sys.all_objects
The convert just gives the month/day. The row number is used to give the numbers 1-8. I multiplied the row number by -1 and added 1 to get the numbers 0,-1,-2,...-7 and date added those (by day) to the first day of this week. I found the first day of this week by taking getdate and date adding the negative version of the day of the week + 1.