Group by start and end of the week date - sql

I have the below promotion dates:
Group: Ambient
Start Date: 03/11/2020 End Date: 09/11/2020
Start Date: 10/11/2020 End Date: 16/11/2020
Group: Chilled
Start Date: 04/11/2020 End Date: 10/11/2020
Start Date: 11/11/2020 End Date: 17/11/2020
etc
My data consists of the below columns:
Dates (Daily), Group (Ambient/Chilled), Net Sales, Volume Sold
I want to group the data like the below:
Weekly Date, Group, Net Sales, Volume Sold
The Weekly date I want the week to start from Tuesday and ending on Monday based on the type of group it is. If the product is chilled I want the week to start from Wednesday and end on Tuesday. I want to do this and include a historical view to compare previous weeks to promotional weeks Example
Week Date Group Net Sales Volume
Row 1 - 27/10/2020 Ambient £900 30
Row 2 - 03/11/2020 Ambient £1000 50
Row 3 - 04/11/2020 Chilled £2000 40
Row 4 - 11/11/2020 Chilled £1000 30
What is the SQL query for changing the week dates?

There might be other ways to do this, but I would it first creating some functions that return me the DOW (Day Of Week) as I wanted (for Tuesday 0, ... Monday 6) and then another function FDOW (First Day Of Week) that use DOW. ie:
-- Tuesday = 0...Monday = 6
CREATE FUNCTION [dbo].[DOW]
(
#date DATETIME
)
RETURNS INT
AS
BEGIN
-- Add the T-SQL statements to compute the return value here
RETURN (##DATEFIRST + DATEPART(dw, #date) - 1 - 2) % 7;
END;
CREATE FUNCTION [dbo].[FDOW]
(
#date DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATEADD(d, -dbo.DOW(#date), CAST(#date AS DATE));
END;
Having such functions it is easy to group on "WeekStart" which is our FDOW function:
Select dbo.FDOW(Dates) as WeekOf, [Group],
sum([Net Sales]) [Net Sales],
sum([Volume Sold]) [Volume]
from myTable
group by dbo.FDOW(Dates) as WeekOf, [Group];

Related

How to convert weekly data into monthly data in sql?

I have a table that has weekly effort hrs and I need to report data on a monthly basis. for most of the cases, the week start date and end date lie in the same month and those records are easy to aggregate but when the week start date and end date don't lie in the same month then I need to break that week's records into 2 rows. One with an actual start date and end of the month as the end date and another record as 1st of next month as the start date and actual end date as the end date.
For the hrs calculation, I need to calculate the number of days that fall in the first month then divide the total hrs by 5 and then multiply by the number of days. and for the second record, it will be total hrs by 5 and then multiply by (5-number of days)
SELECT [USERNAME]
,[EMPLOYEE_NAME]
,[EFFORT_HRS]
,[TS_START_DATE]
,[TS_END_DATE]
FROM [dbo].[Source]
OUTPUT:
USERNAME
EMPLOYEE_NAME
Task
EFFORT_HRS
TS_START_DATE
TS_END_DATE
mk
xyz
abcdefg
40
12/27/2021
1/2/2022
mk
xyz
defgh
33.5
1/31/2022
2/6/2022
mk
xyz
abcdefg
6
4/25/2022
5/1/2022
Expected Result:
USERNAME
EMPLOYEE_NAME
Task
EFFORT_HRS
TS_START_DATE
TS_END_DATE
mk
xyz
abcdefg
40
12/27/2021
12/31/2021
mk
xyz
abcdefg
0
1/1/2022
1/2/2022
mk
xyz
defgh
6.7
1/31/2022
1/31/2022
mk
xyz
defgh
26.8
2/1/2022
2/6/2022
mk
xyz
abcdefg
6
4/25/2022
4/30/2022
mk
xyz
abcdefg
0
5/1/2022
5/1/2022
What you are NOT providing is some sort of calendar associated with holidays such as Jan 1, 2021 (Friday), and Jan 2, 2021 (Saturday) which do not appear to be paid out. That being said, I will leave you to either provide additional information to exclude such dates, or figure out how to adjust the query.
I would also suggest a better storage solution for time be done on a daily basis for easier querying and inclusion / exclusion such as holidays. But, from what is given I have below sample to create and populate table per your sample data.
create table Source
( UserName nvarchar(5),
Employee_Name nvarchar(5),
Task nvarchar(10),
Effort_Hrs numeric( 5, 2),
TS_Start_Date datetime,
TS_End_Date datetime )
insert into Source
( UserName,
Employee_Name,
Task,
Effort_Hrs,
TS_Start_Date,
TS_End_Date)
values
( 'mk', 'xyz', 'abcdefg', 40, '2021-12-27', '2022-01-02' ),
( 'mk',' xyz', 'defgh', 33.5, '2022-01-31', '2022-02-06' ),
( 'mk',' xyz', 'abcdefg', 6, '2022-04-25', '2022-05-01' )
And now the query itself. No matter what you do based of the existing data, you will need a UNION. Basically, selecting the same columns of data in each query. First, get all records once, but chop-off the end date to the last of the month if the start and end dates are different months.
The UNION part will only consider those records where the start and end dates are DIFFERENT months. So, if you had a work week of Jan 5 - Jan 11, you would only see it in the single record with no split, but in your other examples that cross months, you get TWO records. One for the first month, one for the second.
I have rewritten the query based on your feedback of week days and ignoring that of weekends. As such, when computing the pay earned, I had to apply a case/when if PayDays = 0, to just return 0 hours, otherwise you would get a divide by zero error
I am adding extra columns (which you can remove), so you can see how / where the pieces come into play. Now, for ALL records, the start date IS the real basis of the start date in final output. In the first query, its straight-forward, it IS the start date. However, in the UNION portion query, the start date is the first of the month, but again, only in the second query the months are different. So for that, I am doing date add (net subtract) 1 less than the day of the month of the ending date.
I also changed to use EOMONTH() call to compute the end of month for a given date such as in the first part of union where the end date crosses into following month vs the dateadd() originally used.
For the pay days in the UNION part of the query, which only represents entries that cross into the following month, the PayDays IS the ending date Days. So Feb 6th would be 6 days.
NOT month( ts_start_Date ) = month( ts_end_Date )
Here is a function to compute your work days within a given time period begin/end dates that forces cycle through each individual day to determine Mon-Fri.
CREATE Function dbo.WorkDaysInWeek
( #pStartDate as datetime,
#pEndDate as datetime)
RETURNS int
AS
BEGIN
-- if bad start/end date because they passed in end date first, swap them
if( #pStartDate > #pEndDate )
begin
declare #holdDate as DateTime
set #holdDate = #pStartDate
set #pStartDate = #pEndDate
set #pEndDate = #holdDate
end
-- convert to just date to prevent false calculations on time consideration
set #pStartDate = convert( date, #pStartDate)
set #pEndDate = convert( date, #pEndDate )
declare #workDays as int
set #workDays = 0
WHILE ( #pStartDate <= #pEndDate)
BEGIN
-- is the current day being tested a week day vs weekend. Only count Mon-Fri
if( datepart( weekday, #pStartDate) in ( 2, 3, 4, 5, 6 ))
set #workDays = #workDays + 1
set #pStartDate = dateadd( day, 1, #pStartDate )
END
RETURN #workDays
end
GO
Now, instead of computing the datediff in days, you call the function with begin and end dates and the function will cycle through each day individually to determine Mon-Fri only to be counted
Here is the final query.
select
AllWork.*,
case when AllWork.PayDays = 0
then 0.0
else ( AllWork.EFFORT_HRS / ( 1.0 * dbo.WorkDaysInWeek( AllWork.TS_Start_Date, AllWork.TS_End_Date))) * AllWork.PayDays end HoursInPayPeriod
from
(SELECT
USERNAME,
EMPLOYEE_NAME,
EFFORT_HRS,
TS_START_DATE,
TS_END_DATE,
TS_START_DATE as RealStart,
case when month( ts_start_Date ) = month( ts_end_Date )
then ts_end_date
-- simplified date add to use build-in function EOMONTH (End of Month)
else EOMONTH( ts_start_Date, 0) end RealEnd,
dbo.WorkDaysInWeek( TS_START_DATE,
case when month( ts_start_Date ) = month( ts_end_Date )
then ts_end_date
-- simplified date add to use build-in function EOMONTH (End of Month)
else EOMONTH( ts_start_Date, 0) end ) PayDays
FROM
Source
UNION
-- union to get all entries where the end date is a new month from the start
SELECT
USERNAME,
EMPLOYEE_NAME,
EFFORT_HRS,
TS_START_DATE,
TS_END_DATE,
dateadd( day, 1 - datepart( day, ts_end_Date ), ts_end_Date ) RealStart,
ts_end_date RealEnd,
dbo.WorkDaysInWeek( dateadd( day, 1 - datepart( day, ts_end_Date ), ts_end_Date ),
ts_end_date ) PayDays
FROM
Source
where
-- only care about those entries where the start and end date are different months
NOT month( ts_start_Date ) = month( ts_end_Date )
) AllWork

I need Dynamic query in BigQuery to find on which Date the Day Saturday has occurred in First week of Year 2021

I need Dynamic query in BigQuery to find Date of Day Saturday has occurred in First week of Year 2021.
Consider the Day on date of 2022-01-01 is Saturday.
Therefore, I want to extract the date of last Year first week on which the Saturday was occurred.
Try the following:
with sample_dates as (
select date
from unnest(generate_date_array('2022-01-01','2022-12-31')) as date
)
select
date
,(select last_year_date
from unnest(generate_date_array(date_sub(date, INTERVAL 1 YEAR), date-1)) as last_year_date
where extract(week from date) = extract(week from last_year_date)
and extract(dayofweek from date) = extract(dayofweek from last_year_date)
) as last_year_date
from sample_dates
;
Given all of 2022 dates, this provides the same day of week for the same week in the previous year.
For example 2022-01-01 results in 2022-01-02 which is the first Saturday of the first week of last year.
If you're only looking for a single date the main portion would be the generating the appropriate date range with the generate_date_array function and then filtering based on the same week number and dayofweek value.
Use below - should work for any year - just replace 2021 with whatever year you need
select date(2021, 1, 8 - extract(dayofweek from date(2021, 1, 1)))
I think this code can help you!
You can find the first Saturday with a weekly cycle
declare
i number := 0;
date1 date := date '2022-01-01';
d varchar2(20);
begin
while i < 7 loop
SELECT case
when TO_CHAR(date1 + i, 'fmDay') = 'Saturday' then
date1 + i
end "Day"
into d
FROM DUAL;
i := i + 1;
dbms_output.put_line(d);
end loop;
end;

Shipping dates job

I've a job that a need to work with dates, like this:
A ship leaves port of load only from Monday to Saturday, how can I show those dates in a field in sql?
I tried to get weekday and getdate() but I've no sucessfull
CASE
WHEN PORT_ID = 333
THEN CONVERT(VARCHAR(15),DATEADD(D, preview_Date)),103) END AS 'date of load'
but I need the date to always be from Monday to Saturday according to the calendar.
Image Example
CASE
WHEN DATEPART(DW, CONVERT(DATE,departure_Date))) IN (2,3,4,5) AND harbor_id = 412
THEN CONVERT(VARCHAR(15),DATEADD(DAY, -8, CONVERT(DATE,departure_Date)))+ ' TO ' + CONVERT(VARCHAR(15),DATEADD(DAY,-4, CONVERT(DATE,departure_Date)))
END AS 'DEADLINE' ,
Your question is not completely clear to me so I'll make some assumptions. If they are incorrect tell me in the comments, so I can change the answer.
You have a list of departure dates for ships
Customers have 4 workdays (5 normal days if a Sunday is in between) prior to the departure date to deliver goods
Goods can not be delivered on Sunday, ships do not depart on Sundays
example 1: Ship departure = 15/12/2018 (Saturday), date_of_load = 11/12/2018 (Tuesday)
example 2: Ship departure = 17/12/2018 (Monday), date_of_load = 12/12/2018 (Wednesday)
You could solve this with the query below.
It works like this: First set DATEFIRST to 1. SQL uses datefirst to determine how to number the weekdays. Now it numbers Monday as day 1.
Next use the DATEPART function to determine what day of the week the departure date is.
If departure date is a Monday,Tuesday,Wednesday,Thursday (day numbers 2,3,4,5) we subtract 5 days to get the date_of_load, to account for the Sunday. Else we subtract 4.
SET DATEFIRST 1 -- Set monday as day 1
SELECT Ship_Departure_Date,
CASE WHEN DATEPART(dw,Ship_Departure_Date) IN (1,2,3,4) --dw means 'day of the week'
THEN DATEADD(DAY, -5, Ship_Departure_Date)
ELSE DATEADD(DAY, -4, Ship_Departure_Date)
END AS date_of_load
FROM ShipDepartureList
Even if this is not exactly the solution to your problem, I hope this guides you in the right direction.
The with block just generates example data
WITH (
select GetUtcDate() as shipsaildate
Union all
select GetUtcDate() +1
Union all
select GetUtcDate() +2
Union all
select GetUtcDate() +3
Union all
select GetUtcDate() +4
Union all
select GetUtcDate() +5
Union all
select GetUtcDate() +6
Union all
select GetUtcDate() +7
) as departs
Select
shipsaildate,
shipsaildate -
case
when datepart(dw, shipsaildate) >= 6 then datepart(dw, shipsaildate) - 2
else 6
end as deadline
From departs
If the ship departs on mon tue wed or thu then the deadline is 6 days (4 working days) prior. If the ship departs on fri sat or sun, the deadline is 4, 5, or 6 days prior accordingly,
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=0801ac47b0fcd11dfa9c2ee54001fa6a

52weeks rolling with running week data

Can someone suggest me how do we consider week to start on Sunday and end on Saturday, while numbering them backwards in a 52 week rolling report like week1, week2.. week52
I want to count my current week as Week1 starting on Sunday, so even if its partial week its still week1 and last week Sunday-Saturday is week2 and so on until 52nd week last year (that would roughly be in September counting backwards). I need this as I am working on a daily report that will look for sales for current week and past 51 (full) weeks. My report should also return any week without sales '0' without skipping it.
Here is a way. Note I created the recursive CTE to populate some dates. You won't have to do this step, and real only need the YourWeekOrder = ... part.
declare #startDate date = dateadd(year,-1,getdate())
declare #endDate date = getdate()
;with cte as(
select #startDate as TheDate
union all
select dateadd(day,1,TheDate)
from cte
where TheDate < #endDate)
select
TheDate
,TheWeekOfYear = datepart(week,TheDate)
,YourWeekOrder = dense_rank() over (order by cast(datepart(year,TheDate) as char(4)) + case when len(datepart(week,TheDate)) = 1 then '0' + cast(datepart(week,TheDate) as char(2)) else cast(datepart(week,TheDate) as char(2)) end desc)
from cte
order by
TheDate
option(maxrecursion 0)
SEE IT IN ACTION HERE

How to get all the dates in a full calendar month

In a calendar control, we can see some dates from the previous month and next month also. Sample image below
(ie Apr-2016: Starts from Mar-28 and ends in May-08
Mar-2016: Starts from Apr Feb-29 and ends in Apr-10)
Here, i need to generate a list of all the dates in a calendar control for a particular year month. My week start is Monday.
Here is the tsql script i have tried so far.
DECLARE #V_DATE DATE = GETDATE()
;WITH CTE_DATE AS (
SELECT DATEADD(dd,-(DAY(#V_DATE)-1),#V_DATE) CDATE
UNION ALL
SELECT DATEADD(dd,1,CDATE)
FROM CTE_DATE
WHERE DATEADD(dd,1,CDATE) <= DATEADD(dd,-(DAY(DATEADD(mm,1,CDATE))),DATEADD(mm,1,CDATE))
)
SELECT * FROM CTE_DATE
Result Is:
2016-04-01
2016-04-02
.
.
2016-04-29
2016-04-30
It will list all the days from a inputted year month, but i need to include the
missing dates from the previous month as well as next month.
Expected result for Apr-2016
2016-03-28
2016-03-29
.
2016-04-15
.
2016-05-07
2016-05-08
Expected result for May-2016
2016-04-25
2016-04-26
.
2016-05-15
.
2016-06-04
2016-06-05
Note:- The calendar control is always showing 42 days.
since your week is starts on Monday,you can take referece to date 0 '1900-01-01' which is a Monday. Adding 41 days would gives you your end date
select
date_fr = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 0),
date_to = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 41)
the following gives you date 1900-01-01 and Monday
select convert(datetime, 0), datename(weekday, 0)
Have you considered creating a dates table in your database. You would have columns for dates and a column for week number. Linking to this table you could find the week number for your start and end dates, you could then re-link to the table to find the first date of your start week and the last date of your end week. This would probably be more efficient than calculations at each step each time, it is a simple link.
I have create done script for this. This is working as per my expectation, may be helpful for future reference. (Thanks #Squirrel for the logic).
DECLARE #V_ST_DATE DATE = GETDATE()
SET #V_ST_DATE = DATEADD(DAY,-(DAY(#V_ST_DATE)-1),#V_ST_DATE)
SET #V_ST_DATE = DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) +
(CASE WHEN DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) > #V_ST_DATE THEN -7 ELSE 0 END)
;WITH CTE_DATE AS (
SELECT #V_ST_DATE CDATE,0 TDAYS
UNION ALL
SELECT DATEADD(DAY,1,CDATE) , DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE))
FROM CTE_DATE
WHERE DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE)) < 42
)
SELECT * FROM CTE_DATE