SQL Server summarize by week starting monday - sql

I need to summarize a revenue field by week, starting on Monday. My data looks as follows:
Date Rev
+----------+--------
4/10/2017 5
4/11/2017 6
4/12/2017 7
4/13/2017 8
4/17/2017 9
4/19/2017 12
I would like the data to show the first day of the week and the summary of that week like:
Date Rev
+----------+--------
4/10/2017 26
4/17/2017 21
How could I accomplish this?

select DATEADD(week, DATEDIFF(week,0, [date]) , 0) as [date], sum(rev)
from [data]
group by DATEADD(week, DATEDIFF(week,0, [date]) , 0)
order by [date]
SQLFiddle
This maps to your sample data. It works because day 0 (1/1/1900) just happened to fall on a Monday. If you needed to use Sunday or Tuesday as your start of week you would adjust your input dates and offset accordingly.

An example is here, you can use datepart function to easily get the result:
create table #temp
([Date] datetime, Rev int)
insert into #temp values ('2010-01-01',10)
insert into #temp values ('2010-01-02',20)
insert into #temp values ('2010-01-07',60)
insert into #temp values ('2010-01-09',50)
SELECT DATEPART (wk, [Date]) AS Week, Sum(Rev) AS TotalRev
FROM #temp
Group By DATEPART (wk, [Date]);
Edit:
If you are insisting on using the first date value instead of week number, this is an alternative solution for you:
;with cte (firstdateofweek,weekno) as
(
select min([Date]) as firstdateofweek ,DATEPART (wk, [Date]) weekno
from #temp
group by DATEPART (wk, [Date])
)
SELECT cte.firstdateofweek AS Week,sum(Rev) AS Sales
FROM #temp
INNER JOIN cte on DATEPART(wk, [Date]) = cte.weekno
Group By cte.firstdateofweek

Related

Get list of dates that falls in End Of each Month?

I need to get end of each month for the past 3 yrs from the current date excluding statutory holidays and weekends using table1 and table2. Table1 has all the dates ranging from year 2025-2017. Table2 has all the statutory holidays for the years ranging from 2025-2017.
How to create SQL script for to attain this result? Any suggestions could help. Expected result would be list of date last 3yrs of endofmonth excluding statutory holidays and weekends.
Table 1 has 2 columns, DateId and FullDate column
DateID Fulldate
1010392 2019-12-1
1010393 2019-12-2
1010394 2019-12-3
1010395 2019-12-4
.
.
101086 2019-12-31
Table 2 has 2 columns, DateId and Statutory_Holidays
Date ID Stat_Holidays
101085 2019-12-25
101086 2019-12-26
And the returned results should look like
WeekDay_FullDate_Past3yrs
2019-12-31
2020-1-31
2020-2-28
2020-3-31
Tried the below:
select * from
( select a.Date from Table1 a where a.Date <=
'20221215' and a.Date >= DATEADD (YEAR, -3, getdate()) ) as t1
join
( select EOMONTH(a.Date) as Date from Table1 a where a.Date <= '20221215' and a.Date >= DATEADD (YEAR, -3, getdate()) ) as t2 on t1.Date = t2.Date
tried the solution from the below link it dosen't solve my issue. I'm looking to get list of last workday of a month(excluding weekends and holiday) for the past 3yrs
SQL Server - Get Last Business Data excluding holidays and Weekends
You can group by month and year and take the max date (excluding holidays and weekends):
SET DATEFIRST 1;
DECLARE #CurrentDate DATE = '20221215';
WITH cte
AS
(
SELECT MAX(Date ) as EOMDate
FROM Table1
WHERE DATEPART(weekday,Date) NOT IN (6,7)
AND Date NOT IN (SELECT Date FROM Table2)
GROUP BY YEAR(Date),MONTH(Date)
)
SELECT *
FROM cte
WHERE cte.EOMDate BETWEEN DATEADD(YEAR,-3,#CurrentDate) AND #CurrentDate;
This should work and give you the last working day for each month in your main table. Just filter by the desired time period:
SELECT TOP 1 WITH TIES FullDate
FROM Table1
WHERE FullDate NOT IN (SELECT Stat_Holidays FROM Table2) -- not holiday
AND DATEPART(weekday, FullDate) NOT IN (7, 1) -- not saturday and sunday
ORDER BY DENSE_RANK() OVER(PARTITION BY YEAR(FullDate), MONTH(FullDate) ORDER BY FullDate DESC)
Check this with your table name and column names.
select year(dates) _Year ,month(dates) _Month,EOMONTH(dates) endofMOnth from tabledate1 where DATENAME(DW, dates) not in ('Saturday','Sunday')
and EOMONTH(dates) not in (select holidaydate from tableholidays)
Group by year(dates),month(dates),EOMONTH(dates)
order by year(dates) ,month(dates)

Convert date and week of day to calendar format using pivot

I have the following data in the temp table
I want it to convert into the calendar format using pivot as
but due to aggregate it only shows 1 row
SELECT
*
FROM
(SELECT
CONVERT(VARCHAR(10), dt, 106) AS Date1,
dw AS Wd
FROM
#tbl) t
PIVOT
(MAX(Date1)
FOR Wd IN ([Sunday], [Monday], [Tuesday], [Wednesday],[Thursday], [Friday], [Saturday])
) AS pivotTable
as per the comments, I have following update
With CTE (dt,dw,last)
as
(
select Cast(dateadd(day, -day(GetDate())+1,GetDate())as date),datename(dw,Cast(dateadd(day, -day(GetDate())+1,GetDate())as date)),
cast(dateadd(day,-1,dateadd(Month,1,Cast(dateadd(day, -day(GetDate())+1,GetDate())as date))) as date)
union all
select cast(dateadd(day,1,dt) as date), DATENAME(DW,cast(dateadd(day,1,dt) as date)),last
from CTE
where cast(dateadd(day,1,dt) as date)<last
)
select * from (
select dt,dw,ROW_NUMBER()over(partition by dw order by dt) as RN
from CTE
)t
pivot(
Max(dt)
for dw in ([Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday])
) as Pt Option (MAXRECURSION 31)
The problem is that, pivot is not considering the last day of the month for some reason as you can see in the screenshot.
You need to "group by" something unique to the row. In your case, since you want one row per week, that would be the week number.¹ In a pivot table, that "grouping" is achieved by just adding the relevant field to your data source:
SELECT [Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday]
FROM (SELECT dt, dw, DATEPART(wk, dt) AS week_nr
FROM #tbl
) AS t
PIVOT (MAX(dt)
FOR dw in ([Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday])
) AS pivotTable
Result:
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
2022-05-01
2022-05-02
2022-05-03
2022-05-04
2022-05-05
2022-05-06
2022-05-07
2022-05-08
2022-05-09
2022-05-10
2022-05-11
2022-05-12
2022-05-13
2022-05-14
Fiddle: http://sqlfiddle.com/#!18/cdf3e1/6/0
¹ Obviously, if you want your calendar to span multiple years, you'll need the "week year" as well.
Got, the solution, the last day was missing because of the where statement in my CTE, it should be less than or equals to max date.
With CTE (dt,dw,last)
as
(
select Cast(dateadd(day, -day(GetDate())+1,GetDate())as date),datename(dw,Cast(dateadd(day, -day(GetDate())+1,GetDate())as date)),
cast(dateadd(day,-1,dateadd(Month,1,Cast(dateadd(day, -day(GetDate())+1,GetDate())as date))) as date)
union all
select cast(dateadd(day,1,dt) as date), DATENAME(DW,cast(dateadd(day,1,dt) as date)),last
from CTE
where cast(dateadd(day,1,dt) as date)<=last
)
select * from (
select dt,dw,ROW_NUMBER()over(partition by dw order by dt) as RN
from CTE
)t
pivot(
Max(dt)
for dw in ([Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday])
) as Pt Option (MAXRECURSION 31)

How to solve ISO_WEEK partitioning issues for transactions when bridging over two different years

Objective:
I would like to compare records from my table between different years but through the same ISO_WEEK.
Situation:
My issue is that ISO_WEEK sometimes briges between two different years so I can't split correctly the week number from the year. In 2019 for example, ISO_WEEK 1 ranges from Dec 31st 2018 to Jan 6th 2019. The query that follows groups them correctly except for one issue where the partition will group by YEAR then ISO_WEEK, hence excluding Dec 31st for the ISO_WEEK 1. How can I fix this for this specific date but all future dates as well?
My query:
CREATE TABLE [table]
([id] varchar(50), [transaction_date] Datetime)
INSERT INTO [table]
VALUES
(123, '2018-01-01 05:30:00'),
(456, '2018-12-31 08:30:00'),
(456, '2019-01-01 06:30:00'),
(789, '2019-01-02 11:30:00')
SELECT
CAST(t.transaction_date AS Date)
,id
,DATEPART(YEAR, t.transaction_date)
,DATEPART(ISO_WEEK, t.transaction_date)
,ROW_NUMBER()
OVER(PARTITION BY DATEPART(YEAR, transaction_date), DATEPART(ISO_WEEK, transaction_date), id ORDER BY transaction_date) AS week_count
FROM [table] AS t
So if I want to count the ISO_WEEK 1 of 2019 , it will exclude the value of Dec 31st 2018 because of my partitioning. So how can I modifiy my query so that its included?
There is -- alas -- no iso_year date part. You can calculate it with an expression like this:
select year(dateadd(week, 1 - datepart(iso_week, transaction_date), transaction_date))
What this does is go back to the beginning of the year and then adds one week. According to the ISO definition of the year, this will always be in the reference year.
EDIT:
For your query, I would do:
SELECT CAST(t.transaction_date AS Date), id, v.td_iso_year, v.td_iso_week,
ROW_NUMBER() OVER (PARTITION BY id, td.iso_year ORDER BY transaction_date) AS week_count
FROM [table] t CROSS APPLY
(VALUES (YEAR(DATEADD(WEEK, 1 - DATEPART(iso_week, t.transaction_date), t.transaction_date)),
DATEPART(ISO_WEEK, t.transaction_date)
)
) v(td_iso_year, td_iso_week);

get last 3 month on year in sql server

I want to get last 3 months name from current month. For example current month is December. So, I want get like this October, November and December.
This is my query:
SELECT CONVERT(CHAR, DATENAME(MONTH, IssueDate)) AS MonthName, ItemId
FROM dbo.Issue AS Issue
GROUP BY CONVERT(CHAR, DATENAME(MONTH, IssueDate)), ItemId
HAVING (ItemId = 427)
This returns:
But, my need is:
N.B. When December month close and January month open then October auto excluded as like (November, December and January)
this link is my Database only 2 table (size-243 KB with Zip) on the google drive https://goo.gl/S4m0R5
Add a date diff in a where clause to filter to the last 3 months, and then order by the month number at the end:
SELECT CONVERT(CHAR, DATENAME(MONTH, [IssueDate])) AS MonthName, ItemId
FROM [dbo].[Issue] AS Issue
WHERE datediff(m, [IssueDate], getdate()) between 0 and 2
GROUP BY CONVERT(CHAR, DATENAME(MONTH, [IssueDate])), ItemId, MONTH(IssueDate)
HAVING (ItemId= 427)
order by MONTH(IssueDate);
You can use DATEADD function:
WHERE IssueDate >= dateadd( month, -2, dateadd( day, -datepart( day, getdate() ) + 1, cast( getdate() as date ) ) )
That will give you IssueDate >= '2015-10-01' given today.
That will also work with index you have on IssueDate, if you start doing something like DATEADD / DATEDIFF etc. on IssueDate then the index can only be scanned end-to-end because it needs to processs all rows in the table so renders the index significantly less effective.
DECLARE #t TABLE
(
IssueDate DATETIME,
ItemId INT
)
INSERT INTO #t (IssueDate, ItemId)
VALUES
('20160105', 427),
('20151212', 427),
('20151213', 427),
('20151110', 427),
('20151001', 427),
('20150905', 427)
SELECT DATENAME(MONTH, dt)
FROM (
SELECT DISTINCT TOP(3) DATEADD(MONTH, DATEDIFF(MONTH, 0, IssueDate), 0) AS dt
FROM #t
WHERE ItemId = 427
ORDER BY dt DESC
) t
results -
------------------------------
January
December
November
You can use a recursive CTE to get month names for the last 12 months and then limit it to the last 3 month names in the second part of the query:
;WITH months(MonthNumber) AS
(
SELECT 0
UNION ALL
SELECT MonthNumber+1
FROM months
WHERE MonthNumber < 12
)
SELECT DATENAME(MONTH,DATEADD(MONTH,-MonthNumber,GETDATE())) AS [month]
FROM dbo.Issue AS Issue
CROSS JOIN months m
WHERE m.MonthNumber <3
GROUP BY DATENAME(MONTH,DATEADD(MONTH,-MonthNumber,GETDATE())) , ItemId
HAVING (ItemId = 427)

tsql month problem

I have a table like:
id month cost
------------------
1 Jan 200
1 Mar 204
1 May 200
1 Dec 201
I need an output like( order by month including the other months of a year-displaying all 12 months):
to month cost
------------------
1 Jan 200
NULL Feb NULL
1 Mar 204
....
....
....
1 Dec 201
any idea or solution how to do this in TSQL?
Thanks!
edit:: month is extracted from a datetime value.
in real world i'll have to show previous 12 months from last month in a DESC order! any suggestion for that?
Try building a reference table of months, and JOINing on it. It's the quickest way to do this with months in varchar datatype.
declare #foo table (id int, [mon] varchar(100), cost int)
declare #mon table (mon varchar(100), orderWeight int)
INSERT INTO #mon (mon, orderWeight)
VALUES ('Jan',1), ('Feb',2),('Mar',3),('Apr',4),('May',5),('Jun',6),('Jul',7),
('Aug',8),('Sep',9),('Oct',10),('Nov',11),('Dec',12)
INSERT INTO #foo(id, [mon], cost)
VALUES ( 1 ,'Jan' , 200),
( 1 ,'Mar', 204),
( 1 ,'May' , 200),
( 1 ,'Dec' , 201)
select f.id,
m.[mon] ,
f.cost
from #mon as m
left join #foo as f on m.mon = f.mon
order by m.orderWeight
Results:
Your ordering will now be guaranteed with the order by orderWeight.
Sample table
create table mytable(id int, dt datetime, cost money)
insert mytable values
(1,GETDATE()-10,200),
(1,GETDATE()-40,204),
(1,GETDATE()-100,200),
(1,GETDATE()-200,201);
The query, using SQL Server 2008 specific syntax, and sorted properly
select
t.id [to],
CONVERT(char(3),dateadd(month,-M.N,L.PVT),7) [Month],
sum(t.cost) totalCost
from (select PVT=dateadd(month,datediff(month,0,getdate())-1,0)) L
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)) M(N)
left join mytable t
on t.dt >= dateadd(month,-M.N,L.PVT)
and t.dt < dateadd(month,-M.N+1,L.PVT)
group by t.id, right(CONVERT(char(9),dt,6),6), M.N, L.PVT
order by M.N
What it does:
right(CONVERT(char(9),dt,6),6) converts a date into the format 'DD MMM YY', we only need the MMM YY part
In the SELECT, we further extract only the 3-char month from it, using LEFT( , 3)
The subquery L has a single record and column, PVT, which is the first date of the last month
The number series 0-11 is used to create the month values for the last 12 months, using the formula dateadd(month,-M.N,L.PVT)
The range t.dt >= .. and t.dt < .. finds data for a single month
How about this?
The result contains month and year, but you can strip it as you want.
;with months
as
(
select dateadd(month, -1, dateadd(day, datediff(day, 0, getdate()), 0)) as m
union all
select dateadd(month, -1, m)
from months
where m > dateadd(month, -12, getdate())
)
-- Testdata
,yourTable(id,somedate,cost)
as
(
select 1, '2011-01-03', 200
union all
select 1, '2011-03-06', 204
union all
select 1, '2010-05-09', 200
union all
select 1, '2010-05-19', 201
union all
select 1, '2010-12-02', 201
)
-- end testdata
select yt.id
,datename(month,coalesce(yt.somedate, m.m)) as [month]
,datename(year,coalesce(yt.somedate, m.m)) as [year]
--,yt.cost
,sum(yt.cost) as cost
from months m
left join yourTable yt
on datepart(year, yt.someDate) = DATEPART(year, m.m)
and datepart(month, yt.someDate) = DATEPART(month, m.m)
group by
yt.id
,datename(month,coalesce(yt.somedate, m.m))
,datename(year,coalesce(yt.somedate, m.m))
,m.m
order by m.m desc
Edit: Altered solution to support sum.
Remove the group by-section and alter the comment of cost, to get the old solution.