where clause based on a select statement from another table - sql-server

I need help with a query I am writing. I basically want to select all GRNID's from one table but they have to be between dates in another table.
So I want all GRN's between two dates found in the ABSPeriodEndDate table. To find out the start date for the between clause I need to find the MAX Period then minus 1 and the max year.
To find the end date of the between clause I want I need to find both the max period and year. But I want the DateStamp column to return the results for the between clause.
Any ideas on how to resolve this I can't seem to get it working as I want to.
My query is below:
SELECT tblGRNItem.GRNID
FROM tblGRNItem
INNER JOIN ABSPeriodEndDates ON tblGRNItem.DateCreated = ABSPeriodEndDates.DateStamp
WHERE tblGRNItem.DateCreated BETWEEN
SELECT ABSPeriodEndDates.DateStamp FROM ABSPeriodEndDates WHERE ABSPeriodEndDates.DateStamp = (SELECT MAX(ABSPeriodEndDates.Period)-1 FROM ABSPeriodEndDates)
AND ABSPeriodEndDates.Year = (SELECT MAX(ABSPeriodEndDates.Year)))
AND
SELECT ABSPeriodEndDates.DateStamp FROM ABSPeriodEndDates WHERE ABSPeriodEndDates.DateStamp = (SELECT MAX(ABSPeriodEndDates.Period) FROM ABSPeriodEndDates)
AND ABSPeriodEndDates.Year = (SELECT MAX(ABSPeriodEndDates.Year)))
NOTE: DataStamp in ABSPeriodEndDates is a datetime2 data type and the DateCreated in tblGRNItem is a datetime data type.
Example data:
tblGRNItem ABSPeriodEndDates
GRNID || DateCreated Year || Period || DateStamp
1 || 01/01/2015 2015 || 1 || 01/01/2015 00:00:01
2 || 05/01/2015 2015 || 1 || 01/01/2015 00:00:01
3 || 06/02/2015 2015 || 2 || 01/02/2015 00:00:01
4 || 09/02/2015 2015 || 2 || 01/02/2015 00:00:01
5 || 19/02/2015 2015 || 2 || 01/02/2015 00:00:01
6 || 16/03/2015 2015 || 3 || 01/03/2015 00:00:01
So because the greatest period minus one is 2 and the greatest period is 3 I want to get all GRNID's from tblGRNItem between the datestamp 01/02/2015 00:00:01 and 01/03/2015 00:00:01 bearing in mind the datatype's are datetime in tblGRNitem and datetime2 in ABSPeriodEndDates, so the results should be:
3 || 06/02/2015
4 || 09/02/2015
5 || 19/02/2015
Thanks in advance

Thanks Shnugo that worked but I also resolved it by:
declare #StartDate datetime;
set #StartDate= (SELECT Max(DateStamp)
from ABSPeriodEndDates as P
where P.Period = (SELECT Max(Period) -1 from ABSPeriodEndDates)
and P.Year = (SELECT Max(Year) from ABSPeriodEndDates));
declare #EndDate datetime;
set #EndDate= (SELECT top (1) DateStamp
from ABSPeriodEndDates
order by Period desc, DateStamp desc);
SELECT GRNID
from tblGRNItem
where DateCreated between #StartDate and #EndDate;

Without knowing your data it is impossible to give you the answer.
As it seems there is no connection between ABSPeriodEndDates and tblGrnItem. You just want to find the range of the latest periode, true?
You could do something like this
WITH MaxDat AS
(
SELECT MAX(DateStamp) AS MaxDatFound
FROM ABSPeriodEndDates
)
,MinDat AS
(
SELECT TOP 1 DateStamp AS MinDatFound
FROM ABSPeriodEndDates
WHERE DateStamp<(SELECT MaxDatFound FROM MaxDat)
ORDER BY DateStamp DESC
)
SELECT *
FROM tblGRNItem
WHERE DateCreated BETWEEN (SELECT MinDatFound FROM MinDat) AND (SELECT MaxDatFound FROM MaxDat)

Related

Create a select statement that returns a record for each day after a given created date

I have a Dimension table containing machines.
Each machine has a date created value.
I would like to have a Select statement that generates for each day after a certain start date the available number of machines. A machine is available after the date created on wards
As I have read only access to the database I am not able to create a physical calendar table
I hope somebody can help me solving my issue
I assume this is what you want. Based on this sample table:
USE tempdb;
GO
CREATE TABLE dbo.Machines
(
MachineID int,
CreatedDate date
);
INSERT dbo.Machines VALUES(1,'20200104'),(2,'20200202'),(3,'20200214');
Then say you wanted the number of active machines starting on January 1st:
DECLARE #StartDate date = '20200101';
;WITH x AS
(
SELECT n = 0 UNION ALL SELECT n + 1 FROM x
WHERE n < DATEDIFF(DAY, #StartDate, GETDATE())
),
days(d) AS
(
SELECT DATEADD(DAY, x.n, #StartDate) FROM x
)
SELECT days.d, MachineCount = COUNT(m.MachineID)
FROM days
LEFT OUTER JOIN dbo.Machines AS m
ON days.d >= m.CreatedDate
GROUP BY days.d
ORDER BY days.d
OPTION (MAXRECURSION 0);
Results:
d MachineCount
---------- ------------
2020-01-01 0
2020-01-02 0
2020-01-03 0
2020-01-04 1
2020-01-05 1
...
2020-01-31 1
2020-02-01 1
2020-02-02 2
2020-02-03 2
...
2020-02-12 2
2020-02-13 2
2020-02-14 3
2020-02-15 3
Clean up:
DROP TABLE dbo.Machines;
(Yes, some people hiss at recursive CTEs. You can replace it with any number of set generation techniques, some I talk about here, here, and here.)

Calculate and display % change for 2 rows in 3rd row

I have a table to compare Monthly Sales data on the Year-to-Year basis. This table will get updated every month by replacing the records with sales data for last month-same year and the year before.
Month_Year || Sale_Vol || #Sales || Average_Price
Mar-17 || 1250 || 25 || 50
Mar-18 || 900 || 20 || 45
I need to display % change ((Mar-18)/(Mar-17) - 1)*100 in an additional row as shown below
Month_Year || Sale_Vol || #Sales || Average_Price
Mar-17 || 1250 || 25 || 50
Mar-18 || 900 || 15 || 60
Prcnt_Chng || -28 || -40 || 20
I am not able to figure out how to achieve this. This is what I have written so far but it's not elegant.
SELECT 'Percentage Change',NULL,
100.0 * ((select * from Tbl where [Month_Year] = left(CONVERT(VARCHAR(8),DATEADD(mm,-1,DATEADD(mm,DATEDIFF(mm,0,GETDATE()),0)),112),6))-(select * from Tbl where [Month_Year] = left(CONVERT(VARCHAR(8),DATEADD(mm,-1,DATEADD(mm,DATEDIFF(mm,0,GETDATE())-12,0)),112),6)))/(select * from Tbl where [Month_Year] = left(CONVERT(VARCHAR(8),DATEADD(mm,-1,DATEADD(mm,DATEDIFF(mm,0,GETDATE()),0)),112),6));
And also, I get the error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
first of all, reason of your error is, counts are mismatched. i.e 100 is single substance, which is out of subquery. But in-side of subuquery, you return more than one column. if you correct that, you'll you get table(may be or not which will your resulting table)
I trying to query for you..!
Edited:
select * from yourtable -- get previous table
union
select 'Prcnt_Chng ',( -- to get calculated row
((((select Sale_Vol from yourtable where Month_Year = 'Mar-18')/ --value of Sale_Vol of mar 18
(select Sale_Vol from yourtable where Month_Year = 'Mar-17'))-1) --value of Sale_Vol of mar 18. divided and sub by 1
100), -- mul by 100
((((select [#Sales] from yourtable where Month_Year = 'Mar-18')/ --value of [#Sales] of mar 18
(select [#Sales] from yourtable where Month_Year = 'Mar-17'))-1) --value of [#Sales] of mar 18. divided and sub by 1
100), -- mul by 100
((((select Average_Price from yourtable where Month_Year = 'Mar-18')/ --value of Average_Price of mar 18
(select Average_Price from yourtable where Month_Year = 'Mar-17'))-1) --value of Average_Price of mar 18. divided and sub by 1
100)) -- mul by 100
Considering that you have only two rows in your table, you could try this query
;with cte as (
select
*, rn = row_number() over (order by Month_Year)
from
myTable
)
select * from myTable
union all
select
'Prcnt_Chng', 100 * b.Sale_Vol / a.Sale_Vol - 100
, 100 * b.[#Sales] / a.[#Sales] - 100
, 100 * b.Average_Price / a.Average_Price - 100
from
cte a
join cte b on a.rn + 1 = b.rn
where a.rn = 1

SQL query with function

I have a table of data which i am using a count statement to get the amount of records for the submission date
example
AuditId Date Crew Shift Cast ObservedBy 2ndObserver AuditType Product
16 2017-06-27 3 Day B1974, B1975 Glen Mason NULL Identification Billet
20 2017-06-29 1 Day 9879 Corey Lundy NULL Identification Billet
21 2017-06-29 4 Day T9627, T9625 Joshua Dwyer NULL ShippingPad Tee
22 2017-06-29 4 Day NULL Joshua Dwyer NULL Identification Billet
23 2017-06-29 4 Day S9874 Joshua Dwyer NULL ShippingPad Slab
24 2017-06-29 4 Day Bay 40 Joshua Dwyer NULL Identification Billet
Basically I am using the following code to get my results
SELECT YEAR([Date]) as YEAR, CAST([Date] as nvarchar(25)) AS [Date], COUNT(*) as "Audit Count"
FROM AuditResults
where AuditType = 'Identification' AND Product = 'Billet'
group by Date
this returns example
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-29 3
Now I want to be able to retrieve all dates even if blank
so I would like the return to be
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-28 0
2017 2017-06-29 3
I have the following function I am trying to use:
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #DateList TABLE (Dt date)
AS
BEGIN
DECLARE #TotalDays int, #DaysCount int
SET #TotalDays = DATEDIFF(dd,#FromDate,#ToDate)
SET #DaysCount = 0
WHILE #TotalDays >= #DaysCount
BEGIN
INSERT INTO #DateList
SELECT (#ToDate - #DaysCount) AS DAT
SET #DaysCount = #DaysCount + 1
END
RETURN
END
How do I use my select statement with this function? or is there a better way?
cheers
Try this;
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #YourData TABLE ([Year] int, DateText nvarchar(25),[Audit Count] int)
AS
begin
insert into #YourData
SELECT
YEAR(allDates.[Date]) as YEAR,
CAST(allDates.[Date] as nvarchar(25)) AS [Date],
COUNT(r.Product) as "Audit Count"
from
(
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
)allDates
left join
AuditResults r on r.[Date]=allDates.[date] and r.AuditType = 'Identification' AND r.Product = 'Billet'
where
allDates.[Date]>=#FromDate and allDates.[Date]<=#ToDate
group by
allDates.[Date]
return
end
The key is the 'allDates' section ;
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
This will return all dates between 1900 and 2173 (in this example). Limit that as you need but a nice option. A ton of different ways to approach this clearly
you have to create another table calendar as (Mysql)- idea is the same on all RDBMS-
CREATE TABLE `calendar` (
`dt` DATE NOT NULL,
UNIQUE INDEX `calendar_dt_unique` (`dt`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
and fill with date data.
more details

Convert rows to columns for the CALENDAR_DATE with respect to Month Name and Year

I am new to SQL Server and I am facing this trouble please need your help on this.
I am using SQL Server 2008
I have two tables
table 1: CALENDAR_DIMENSION
Row_Seq(numeric(5,0), null)
CALENDAR_YEAR(int, null)
CALENDAR_MONTH_NO(int, null)
CALENDAR_MONTH_NAME(varchar(15), null)
CALENDAR_DATE(date, null)
table 2: HOLIDAY_DETAILS
CALENDAR_DATE(date, not null)
DESCRIPTION (varchar(50), null)
CALENDAR_DAY_NAME(varchar(15), null)
IS_WORKING_DAY(int, null)
My requirement is to get the data from the two tables as below for the CALENDAR_DATE it should pick the values from HOLIDAY_DETAILS table from the columnn IS_WORKING_DAY (0= Holiday, 1= working day)
Calendar_Year Calendar_Month_No Calendar_Date
2014 1 01-01-2014 02-01-2014 03-01-2014 04-01-2014 05-01-2014 ……. 31-01-2014
2014 2 01-02-2014 02-02-2014 03-02-2013 03-02-2013 04-02-2013 ……. 28-02-2014
2014 3 01-03-2014 02-03-2014 03-03-2014 04-03-2014 05-03-2014 ……. 31-03-2014
Result should be in this manner after Joining to CALENDAR_DIMENSION. First I need all the CALENDAR-YEAR, CALENDAR_MONTH and CALENDAR_DAYS in horizontal ways as shown in the example below from HOLIDAY_DETAILS table and then would like to join to table CALENDAR_DIMENSION to get the IS_WORKING_DAY status on the Days.
Calendar_Year Calendar_Month_No Calendar_Date
2014 1 1 1 1 0 0 ……. 1
2 0 0 1 0 0 ……. 1
3 0 0 1 0 0 ……. 1
Working Day = 1
Holiday = 0
according to me YEAR_MONTH-DAY is useless.i will not create this table .you can verify my script with your requirement and tell how it won't work.after verification you can easily join with holiday table.
Declare #year varchar(4)=2014
;with cte as
(
select #year calyear,1 monthno,cast(#year+'-'+'1'+'-'+'1' as date) as date1,cast(#year+'-'+'1'+'-'+'2' as date) date2,cast(#year+'-'+'1'+'-'+'3' as date) date3,cast(#year+'-'+'1'+'-'+'4' as date) date4,cast(#year+'-'+'1'+'-'+'5' as date) date5,cast(#year+'-'+'1'+'-'+'6' as date) date6,cast(#year+'-'+'1'+'-'+'7' as date) date7
union all
select calyear,monthno+1,dateadd(month,1, date1),dateadd(month,1, date2),dateadd(month,1, date3),dateadd(month,1, date4),dateadd(month,1, date5),dateadd(month,1, date6),dateadd(month,1, date7) from cte where calyear=2014 and monthno<=11
)
select * from cte

Apply week number to dates for whole weeks only

The time is: (m/d/yyyy) => 2009/01/04
Using this command using datepart(wk,'20090104') I can get the week number (for any given date).
So :
SELECT datepart(wk,'20090101') //1
SELECT datepart(wk,'20090102') //1
SELECT datepart(wk,'20090103') //1
SELECT datepart(wk,'20090104') //2
So far so good.
The problem :
Those 3 first dates are not part of a full week, so I can't put them in a fixed 52-week chart.
Our company needs to see information about each whole week in the 52 weeks of a year. (Each year has 52 whole weeks).
So 20090101 doesn't belong to the first week of 2009 !
It belongs to the previous year (which is irrelevant to my question)
So I need a UDF (I've been searching a lot, and ISOWEEK is not answering my needs) which by a given datetime, will give me the Week Number (week = whole week, so partial weeks aren't considered).
Example :
calcweekNum ('20090101') //52 ...from the last year
calcweekNum ('20090102') //52 ...from the last year
calcweekNum ('20090103') //52 ...from the last year
calcweekNum ('20090104') //1
..
..
calcweekNum ('20090110') //1
calcweekNum ('20090111') //2
calcweekNum ('20090112') //2
...
Here's a different approach. All you need to supply is the year:
DECLARE #year INT = 2009;
DECLARE #start SMALLDATETIME;
SET #start = DATEADD(YEAR, #year-1900, 0);
;WITH n AS
(
SELECT TOP (366) -- in case of leap year
d = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY name)-1, #start)
FROM sys.all_objects
),
x AS
(
SELECT md = MIN(d) FROM n
WHERE DATEPART(WEEKDAY, d) = 1 -- assuming DATEFIRST is Sunday
),
y(d,wk) AS
(
SELECT n.d, ((DATEPART(DAYOFYEAR, n.d) - DATEDIFF(DAY, #start, x.md)-1)/7) + 1
FROM n CROSS JOIN x
WHERE n.d >= x.md
AND n.d < DATEADD(YEAR, 1, #start)
)
SELECT [date] = d, [week] = wk
FROM y WHERE wk < 53
ORDER BY [date];
Results:
date week
---------- ----
2009-01-04 1
2009-01-05 1
2009-01-06 1
2009-01-07 1
2009-01-08 1
2009-01-09 1
2009-01-10 1
2009-01-11 2
2009-01-12 2
...
2009-12-25 51
2009-12-26 51
2009-12-27 52
2009-12-28 52
2009-12-29 52
2009-12-30 52
2009-12-31 52
Note that week 52 won't necessarily be a full week, and that in some cases (e.g. 2012), the last day or two of the year might fall in week 53, so they're excluded.
An alternative approach is to repeat the MIN expression twice:
DECLARE #year INT = 2009;
DECLARE #start SMALLDATETIME;
SET #start = DATEADD(YEAR, #year-1900, 0);
;WITH n AS
(
SELECT TOP (366) -- in case of leap year
d = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY name)-1, #start)
FROM sys.all_objects
),
y(d,wk) AS
(
SELECT n.d, ((DATEPART(DAYOFYEAR, n.d) - DATEDIFF(DAY, #start, (SELECT MIN(d)
FROM n WHERE DATEPART(WEEKDAY, d) = 1))-1)/7) + 1
FROM n
WHERE n.d >= (SELECT md = MIN(d) FROM n WHERE DATEPART(WEEKDAY, d) = 1)
AND n.d < DATEADD(YEAR, 1, #start)
)
SELECT [date] = d, [week] = wk
FROM y WHERE wk < 53
ORDER BY d;
Here's a function for you to calculate it on the fly:
CREATE FUNCTION dbo.WholeWeekFromDate (
#Date datetime
)
RETURNS tinyint
AS BEGIN
RETURN (
SELECT DateDiff(Day, DateAdd(Year, DateDiff(Year, 0, CalcDate), 0), CalcDate) / 7 + 1
FROM (SELECT DateAdd(Day, (DateDiff(Day, 0, #Date) + 1) / 7 * 7, 0)) X (CalcDate)
);
END;
I don't recommend you use it, as it may perform badly due to being called once for every row. If you absolutely must have a function to use in real queries, then convert it to an inline function returning a single column and row, and use it as so:
SELECT
OtherColumns,
(SELECT WeekNumber FROM dbo.WholeWeekFromDate(DateColumn)) WeekNumber
FROM
YourTable;
This will allow it to be "inlined" in the execution plan and perform significantly better.
But even better, as others have suggested, is to use a BusinessDate table. Here's a head start on creating one for you:
CREATE TABLE dbo.BusinessDate (
BusinessDate date NOT NULL CONSTRAINT PK_BusinessDate PRIMARY KEY CLUSTERED,
WholeWeekYear smallint NOT NULL
CONSTRAINT CK_BusinessDate_WholeWeekYear_Valid
CHECK (WholeWeekYear BETWEEN 1900 AND 9999),
WholeWeekNumber tinyint NOT NULL
CONSTRAINT CK_BusinessDate_WholeWeekNumber_Valid
CHECK (WholeWeekNumber BETWEEN 1 AND 53),
Holiday bit CONSTRAINT DF_BusinessDate_Holiday DEFAULT (0),
Weekend bit CONSTRAINT DF_BusinessDate_Weekend DEFAULT (0),
BusinessDay AS
(Convert(bit, CASE WHEN Holiday = 0 AND Weekend = 0 THEN 1 ELSE 0 END)) PERSISTED
);
And I'll even populate it from 1900-01-01 through 2617-09-22 (Is that enough for the projected life of your product? And it's only 7.8MB so don't fret over size):
WITH A (N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
B (N) AS (SELECT 1 FROM A F, A A, A L, A C, A O, A N),
C (N) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM B),
Dates AS (
SELECT
N,
DateAdd(Day, N, '18991231') Dte,
DateAdd(Day, N / 7 * 7, '19000101') CalcDate
FROM C
)
INSERT dbo.BusinessDate
SELECT
Dte,
Year(CalcDate),
DateDiff(Day, DateAdd(Year, DateDiff(Year, 0, CalcDate), 0), CalcDate) / 7 + 1,
0,
(N + 6) % 7 / 5 -- calculate weekends
FROM Dates; -- 3-7 seconds or so on my VM server
Then join to the table on the date, and use the WholeWeekNumber column for your output. You might also consider adding a WeekNumberYear because it's going to be a tad difficult to figure out that the 52 of 2009-01-01 really belongs to 2008 without this... a strange data point in there for sure if you don't (laugh).
Example table contents:
BusinessDate WholeWeekYear WholeWeekNumber Holiday Weekend BusinessDay
------------ ------------- --------------- ------- ------- -----------
1/1/2009 2008 52 0 0 1
1/2/2009 2008 52 0 0 1
1/3/2009 2008 52 0 1 0
1/4/2009 2009 1 0 1 0
1/5/2009 2009 1 0 0 1
1/6/2009 2009 1 0 0 1
1/7/2009 2009 1 0 0 1
1/8/2009 2009 1 0 0 1
1/9/2009 2009 1 0 0 1
1/10/2009 2009 1 0 1 0
1/11/2009 2009 2 0 1 0
If you really don't want to use this as a general business date calculation table, you can drop the last 3 columns, otherwise, update the Holiday column to 1 for company holidays.
Note: if you actually make the above table, and your access to it most often uses JOIN or WHERE conditions on a different column than BusinessDate, then make the primary key nonclustered and add a clustered index starting with the alternate column.
Some of the above scripts require SQL 2005 or higher.
It would be relatively easy to setup a custom calendar table with one row for each date of the year in it, and then have other fields that will allow you to rollup however you want. I do this when I have clients using varying calendars, i.e. fiscal years, and it makes the query logic very simple.
Then you just join date-to-date and get the week-number that you want.
date | reporting year | reporting week
-----------|----------------|---------------
2009-01-01 | 2008 | 52
2009-01-02 | 2008 | 52
2009-01-03 | 2008 | 52
2009-01-04 | 2009 | 01
2009-01-05 | 2009 | 01
etc.
and then to use it ( for example to get total sales rollup by your custom weeks, didn't validated my sql):
select reporting_year, reporting_month, sum(sales)
from sales
inner join custom_date_table cdt on cdt.sysdate = sales.sysdate
group by reporting_year, reporting_month
where report_year=2009
DECLARE #StartDate DATE;
SET #StartDate = '20120101';
WITH Calendar AS (
SELECT #StartDate AS DateValue
,DATEPART(DW, #StartDate) AS DayOfWeek
,CASE WHEN DATEPART(DW, #StartDate) = 1 THEN 1 ELSE 0 END AS WeekNumber
UNION ALL
SELECT DATEADD(d, 1, DateValue)
,DATEPART(DW, DATEADD(d, 1, DateValue)) AS DayOfWeek
,CASE WHEN DayOfWeek = 7 THEN WeekNumber + 1 ELSE WeekNumber END
FROM Calendar
WHERE DATEPART(YEAR, DateValue) = DATEPART(YEAR, #StartDate)
)
SELECT DateValue, WeekNumber
FROM Calendar
WHERE WeekNumber BETWEEN 1 AND 52
AND DATEPART(YEAR, DateValue) = DATEPART(YEAR, #StartDate)
OPTION (MAXRECURSION 0);
Don't use a UDF, use a calendar table instead, then you can define week numbers exactly as your company requires and simply query them from the table, which will be much easier and possibly much faster than using a UDF.
A calendar table has numerous uses in SQL (search this site or Google) so you should probably create it anyway.
There is no good answer for this.
A year is NOT 52 weeks long.
It is 52 weeks and one day in normal years, and 52 weeks and two days in leap years.

Resources