Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 
        
Home       Members    Calendar    Who's On


Add to briefcase ««1234»»»

4-4-5 Calendar Functions, Part 1 Expand / Collapse
Author
Message
Posted Monday, September 28, 2009 10:53 PM


Valued Member

Valued MemberValued MemberValued MemberValued MemberValued MemberValued MemberValued MemberValued Member

Group: General Forum Members
Last Login: Wednesday, December 15, 2010 6:35 PM
Points: 63, Visits: 193
thomasrawley, while i detest your naming conventions, how many compilers care if the name is fscl_yr, how many compilers care if the name is [Fiscal Year] ... go on bite me Jeff,

the join table is excellent.

joe.rojas, the CFO, ultimately, signs the cheques, very good call.

Joe Celko, coooool, I like it when temporal gets used in context. Like, in the context of, "we do [pick one] [DP, IM, IT, ITC, [iterate vowel year plus RAND()-3, consonant year plus RAND()+2)], etc]

Jeff, are you actually against calendar tables or are you just playing the game? I've tried to build calendar functions that allow for the various changes that Popes required to be implemented for the Gregorian calendar in order to allow for various mid-European country changes that were out of the Papally-prescribed sequence, which is a very cool but in the majority of cases outside of university research, a complete waste of time, ... and the join table is really easy as a solution.

Maybe an MS maintained CLR function could be better, but I'm way too far from MS Central to comment on that idea.

Editted to fix missing ]'s, Doh.


Peter Edmunds ex-Geek
Post #794906
Posted Monday, September 28, 2009 11:51 PM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 4:24 PM
Points: 36,995, Visits: 31,522
wldhrs (9/28/2009)
thomasrawley, while i detest your naming conventions, how many compilers care if the name is fscl_yr, how many compilers care if the name is [Fiscal Year] ... go on bite me Jeff,


Heh... bite yourself... I don't care what you call your stuff. I'd much rather just see FiscalYear instead of the abbreviations or the report formatted column name, but whatever.

Jeff, are you actually against calendar tables or are you just playing the game?

Oh heck no... not against calendar tables at all. I just see all those folks talking about them but no code to help others out.

Editted to fix missing ]'s, Doh.

Heh... see?


--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #794917
Posted Tuesday, September 29, 2009 3:20 AM
SSC Rookie

SSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC Rookie

Group: General Forum Members
Last Login: Tuesday, May 6, 2014 11:31 AM
Points: 30, Visits: 77
Here's the approach that I use - with a bit of code - and that has served me well in a number of UK-based organisations.

For background, UK fiscal years can start almost anywhere in the "real" calendar - my current organisation begins and ends theirs on the 4th Sunday in September (so the current year, just begun - 2009/10 - is a "53" week one). Quarter/Period patterns are generally 4-4-5, except that in a 53-week year, one of them will be either a 4-5-5 or a 5-4-5 one. The problem here is that the determination of which quarter is to get that treatment, and what the treatment is, is determined by the executives relatively close to the time so it is not practical to load the calander table too far ahead!

The approach I use has a "seed" table with 371 (i.e. 53 weeks worth) or days in it. This has five columns:

pkintSeedDay - day of the fiscal year (1 thru 371)
intSeedPeriod - corresponding period for that day if a 52 week year
intSeedWeekOfPeriod - corresponding week of the period for that day if a 52 week year
intSeedPeriod53 - corresponding period for that day if a 53 week year
intSeedWeekOfPeriod53 - corresponding week of the period for that day if a 53 week year

You can set this up manually or write a bit of code to do so - but its done once only.

The main calendar table has eleven columns (you can remove any you don't want or add any that would be of value) as follows:

pkdatDate - the actual date
intDayOfFY - the day within the fiscal year - 1 to 364 or 371
intWeekOfPeriod - the week within the period - 1 to 4 or 5
intPeriodOfFY - the relevent period - 1 to 12
intQuarterofFY - the relevent quarter - 1 to 4
intFYID - a value that can be used to get the textual name of the financial year from a lookup table (eg fiscal year "9" may lookup the text "2009/2010") for reporting
intWeekOfFY - the week number within the fiscal year - 1 to 52 or 53
intDayOfWeek - the day within the company week - 1 to 7 (see note below)
intWeeksAgo - this and the next two columns are updated daily (see notes below)
intDaysAgo -
intPeriodsAgo -

Note regarding intDayOfWeek: this is stored because use of DATEPART to get the same thing is langauage/locale dependent whereas the company has its own rigid interpretation of when a week starts and ends.
Note regarding intDaysAgo: this is set to a zero if this row reflects todays date, with positive numbers representing days already past and negative numbers representing days in the future.
Note regarding intWeeksAgo: this is set to a zero if this row is in the same fiscal week as todays date, with positive numbers representing weeks already past and negative numbers representing weeks in the future.
Note regarding intPeriodsAgo: this is set to a zero if this row is in the same fiscal period as todays date, with positive numbers representing periods already past and negative numbers representing periods in the future.

Each year, the following code is executed (after making any required changes) to load a new years worth of rows into the calandar table using the feeder table as a base. If it is a 53-week year (and the "extra" week has moved since the last time a 53-week year was loaded) then the feeder table will need to be realigned first using the code at the foot of this article.

USE EXTRACT;
DECLARE @startdate datetime, @fy int, @fyname char(7), @days int
--
-- Set the parameters below to the required values
--
-- SET @startdate = '2007-09-30'
SET @startdate = (SELECT MAX(pkdatDate)+1 FROM tblFinancialCalendar)
-- Comment out one of the above two lines:
-- use the first if you want to set a fixed start date and use the second if you want to append a new year to the end of the existing table
SET @fy = 9
SET @fyname = 'FY09/10'
SET @days = 371
-- @days must be set to 364 for a 52 week year, or set to 371 for a 53 week year
-- ( but check that the additional week is in the correct period in the
-- CalendarFeeder table first or run prcCalendarFeeder53Setup to reset it )
--
-- Load the new year into the main table from the feeder table
--
INSERT INTO tblFinancialCalendar
SELECT
@startdate-1+pkintSeedDay, -- pkdatDate
pkintSeedDay, -- intDayOfFY
CASE WHEN @days=364 THEN intSeedWeekOfPeriod ELSE intSeedWeekOfPeriod53 END, -- intWeeekOfPeriod
CASE WHEN @days=364 THEN intSeedPeriod ELSE intSeedPeriod53 END, -- intPeriodOfFY
CASE
WHEN intSeedPeriod>= 1 and intSeedPeriod<=3 THEN 1
WHEN intSeedPeriod>= 4 and intSeedPeriod<=6 THEN 2
WHEN intSeedPeriod>= 7 and intSeedPeriod<=9 THEN 3
WHEN intSeedPeriod>= 10 and intSeedPeriod<=12 THEN 4
END, -- intQuarterOfFY
@fy, -- intFYID
FLOOR((pkintSeedDay-1)/7)+1, -- intWeekOfFY
NULL, -- intDayOfWeek (set below)
NULL, -- intWeeksAgo (reset daily)
NULL, -- intDaysAgo (reset daily)
NULL -- intPeriodsAgo (reset daily)
FROM tblCalendarFeeder
WHERE pkintSeedDay<=@days;
--
-- Update the calandar to set the "day of the week" values
-- (These are used to prevent issues caused by the DATEPART(dw,xxx) function which gives variable outputs dependent on language
-- and other settings that may be in effect at a per-user level)
--
SET DATEFIRST 7; -- Our financial weeks run from Sunday to Saturday
UPDATE tblFinancialCalendar SET intDayOfWeek=DATEPART(dw,pkdatDate);
--
-- Add or update new year into the Financial Year Names table
--
DELETE FROM tblCalendarFinancialYearNames WHERE pkintFYID=@fy;
INSERT INTO tblCalendarFinancialYearNames
SELECT
@fy,
@fyname;


The table can now be joined to any date elsewhere in the database to obtain fiscal period data.

Each night the "DaysAgo", "WeeksAgo" and "PeriodsAgo" columns are recalculated using the following code:.

UPDATE tblFinancialCalendar SET 
intWeeksAgo=DATEDIFF(wk,pkdatDate,GETDATE());
UPDATE tblFinancialCalendar SET
intDaysAgo=DATEDIFF(dy,pkdatDate,GETDATE());
UPDATE tblFinancialCalendar SET
intPeriodsAgo=(SELECT COUNT(DISTINCT intFYID*100+intPeriodOfFY)-1
FROM tblFinancialCalendar b
WHERE pkdatDate BETWEEN tblFinancialCalendar.pkdatDate AND GETDATE())
WHERE tblFinancialCalendar.pkdatDate<=GETDATE();
UPDATE tblFinancialCalendar SET
intPeriodsAgo=(SELECT -COUNT(DISTINCT intFYID*100+intPeriodOfFY)+1
FROM tblFinancialCalendar b
WHERE pkdatDate BETWEEN GETDATE() AND tblFinancialCalendar.pkdatDate)
WHERE tblFinancialCalendar.pkdatDate>GETDATE();

These columns can then be used to select data from tables to which the calandar has been joined, for reporting perposes. For example, you could select all data from the previous period by adding a "WHERE intPeriodsAgo=1" clause.

Finally, here is the code that I use to reset the 53rd week if it has moved since the last time a 53-week year was added:

DECLARE @period int,@cut int;
--
-- Set the following parameter before running
--
SET @period=11;
-- Enter the period which is to have the extra 5th week (should be one of 1,2,4,5,7,8,10 or 11)
SET @cut=(SELECT MAX(pkintSeedDay) FROM tblCalendarFeeder WHERE intSeedPeriod=@period);
CREATE TABLE tblCalendarFeeder2
(pkintSeedDay INT NOT NULL PRIMARY KEY,intSeedPeriod INT NOT NULL,intSeedWeekOfPeriod INT NOT NULL);
UPDATE tblCalendarFeeder SET intSeedPeriod53=NULL,intSeedWeekOfPeriod53=NULL;
INSERT INTO tblCalendarFeeder2 SELECT pkintSeedDay,intSeedPeriod,intSeedWeekOfPeriod FROM tblCalendarFeeder;
UPDATE tblCalendarFeeder SET
intSeedPeriod53=(select intSeedPeriod from tblCalendarFeeder2 b
where tblCalendarFeeder.pkintSeedDay=b.pkintSeedDay and tblCalendarFeeder.pkintSeedDay<=@cut),
intSeedWeekOfPeriod53=(select intSeedWeekOfPeriod from tblCalendarFeeder2 b
where tblCalendarFeeder.pkintSeedDay=b.pkintSeedDay and tblCalendarFeeder.pkintSeedDay<=@cut)
WHERE tblCalendarFeeder.pkintSeedDay<=@cut;
UPDATE tblCalendarFeeder SET
intSeedPeriod53=(select intSeedPeriod from tblCalendarFeeder2 b
where tblCalendarFeeder.pkintSeedDay=b.pkintSeedDay+7 and tblCalendarFeeder.pkintSeedDay>@cut+7),
intSeedWeekOfPeriod53=(select intSeedWeekOfPeriod from tblCalendarFeeder2 b
where tblCalendarFeeder.pkintSeedDay=b.pkintSeedDay+7 and tblCalendarFeeder.pkintSeedDay>@cut+7)
WHERE tblCalendarFeeder.pkintSeedDay>@cut+7;
UPDATE tblCalendarFeeder SET intSeedPeriod53=@period,intSeedWeekOfPeriod53=5 WHERE intSeedPeriod53 IS NULL;
DROP TABLE tblCalendarFeeder2;

Post #794978
Posted Tuesday, September 29, 2009 7:30 AM
Valued Member

Valued MemberValued MemberValued MemberValued MemberValued MemberValued MemberValued MemberValued Member

Group: General Forum Members
Last Login: Monday, August 25, 2014 10:11 PM
Points: 56, Visits: 1,043
Jeff Moden (9/28/2009)
Heh... I see 3 recommendations to build a Calendar table and two that claim they've used one, yet no one has offered up any of their own code to show they have ever done so. Like the tag line from the movie goes... Show Me the Money!


Jeff - did you count my rambling on about week numbers as one of those recommendations ?

if so, you're making me feel all guilty and I'll have to go off and try and find a copy of that database in an archive somewhere.....

/Ryan
Post #795117
Posted Tuesday, September 29, 2009 9:17 AM
SSC Rookie

SSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC Rookie

Group: General Forum Members
Last Login: Monday, June 2, 2014 8:40 AM
Points: 39, Visits: 296
I've seen several replies about using a table to store all of the data that I might need. I have explored this as an option and I definitely think it is a possibility. As Joe Celko pointed out my approach is not SET based and SQL server does like its sets. I try to use them when I can and if you can implement this idea with sets I'd say that's a great way to go. I'll probably even do a table implementation myself.

One thing missing from the table suggestions is the idea that the starting day of the week could change. The starting day could be Sunday for one company and Monday for another company. You could still build a table to take this into account. Let me know if I'm missing something and the tables do take this into account.

The comment from Ryan Price is also interesting. I'd like to see some performance metrics on date range joins.

In the end, I came up with a little function that worked for me and wanted to share it since I had a lot of trouble finding much out there. I'm going to share my function for getting a Period (like a month), which is also procedural based. For a table implementation maybe my functions could be modified to populate a table. Sounds like I or someone could even do a comparison article on function based, set based, and various join methods. That could be fun.

Thanks everyone for your comments. Keep them coming.



Post #795222
Posted Tuesday, September 29, 2009 10:02 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 4:24 PM
Points: 36,995, Visits: 31,522
Ryan C. Price (9/29/2009)
Jeff Moden (9/28/2009)
Heh... I see 3 recommendations to build a Calendar table and two that claim they've used one, yet no one has offered up any of their own code to show they have ever done so. Like the tag line from the movie goes... Show Me the Money!


Jeff - did you count my rambling on about week numbers as one of those recommendations ?

if so, you're making me feel all guilty and I'll have to go off and try and find a copy of that database in an archive somewhere.....

/Ryan


Heh... I guess my comment worked. Good folks are coughing up some good stuff. Thank you one and all.


--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #795266
Posted Tuesday, September 29, 2009 10:04 AM


SSC-Dedicated

SSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-DedicatedSSC-Dedicated

Group: General Forum Members
Last Login: Today @ 4:24 PM
Points: 36,995, Visits: 31,522
andyscott (9/29/2009)
Here's the approach that I use - with a bit of code - and that has served me well in a number of UK-based organisations.


Very cool. That's a heck of an explanation. Thanks for taking the time.


--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

(play on words) "Just because you CAN do something in T-SQL, doesn't mean you SHOULDN'T." --22 Aug 2013

Helpful Links:
How to post code problems
How to post performance problems
Post #795267
Posted Tuesday, September 29, 2009 10:38 AM
SSC-Addicted

SSC-AddictedSSC-AddictedSSC-AddictedSSC-AddictedSSC-AddictedSSC-AddictedSSC-AddictedSSC-Addicted

Group: General Forum Members
Last Login: Wednesday, November 17, 2010 3:38 AM
Points: 445, Visits: 82
How common is this 4-4-5 (or variant) thinking in modern accounting systems? This is the first I've heard of it and I've DBA'ed behind three different fairly popular accounting systems, none of which incorporated this possibility. The assumption was always that a period was monthly.

Then my second question is that every 5 or 6 years you'll need an extra week thrown in somewhere. Do the accountants just make a 4-5-5 in a fiscal quarter?



Post #795291
Posted Tuesday, September 29, 2009 11:37 AM
SSC Rookie

SSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC Rookie

Group: General Forum Members
Last Login: Monday, June 2, 2014 8:40 AM
Points: 39, Visits: 296
drnetwork (9/29/2009)
How common is this 4-4-5 (or variant) thinking in modern accounting systems? This is the first I've heard of it and I've DBA'ed behind three different fairly popular accounting systems, none of which incorporated this possibility. The assumption was always that a period was monthly.

Then my second question is that every 5 or 6 years you'll need an extra week thrown in somewhere. Do the accountants just make a 4-5-5 in a fiscal quarter?


I have no idea how common it is but we had been running our application for about 10 years before it came up so we had to come up with a solution. And I think, non-accountant that I am, that every few years there is an extra week thrown in. I mention this in my article and andyscott mentions it, too.

A further question I have on a table implementation (yes, I think it's a great idea but I'm pondering things) is what about shifting the start date of the year. Meaning, if you build the table where year 2008 starts on 1/6/2008 then the table's data is fine. But what happens if the accountants decide that the year should start on the first Sunday after Sept 1st? Then another accountant says it should start on the first Tuesday after July 1st? I'm thinking you'd need more key values in the table but I'm curious if anyone has any great implementation ideas.

Cliff



Post #795340
Posted Wednesday, September 30, 2009 3:26 AM
SSC Rookie

SSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC RookieSSC Rookie

Group: General Forum Members
Last Login: Tuesday, May 6, 2014 11:31 AM
Points: 30, Visits: 77
corder (9/29/2009)[hr
I have no idea how common it is but we had been running our application for about 10 years before it came up so we had to come up with a solution. And I think, non-accountant that I am, that every few years there is an extra week thrown in. I mention this in my article and andyscott mentions it, too.

A further question I have on a table implementation (yes, I think it's a great idea but I'm pondering things) is what about shifting the start date of the year. Meaning, if you build the table where year 2008 starts on 1/6/2008 then the table's data is fine. But what happens if the accountants decide that the year should start on the first Sunday after Sept 1st? Then another accountant says it should start on the first Tuesday after July 1st? I'm thinking you'd need more key values in the table but I'm curious if anyone has any great implementation ideas.


I think it depends on the nature of your company's business - if business is very geared to a weekly cycle, then the imposition of the "calendar month" as a "period" is too inflexible. Hence - if your business still wishes to report on a pseudo-monthly cycle, ie twelve times in a year - the need for fiscal periods each of an integral number of weeks. But the definition of the year itself - in terms of when it starts and ends and, in some instances, what any interim points are - is (at least for a UK-based, publically listed company) strictly controlled, since it is a requirement of its registration on the stock market. So its not just down to the accountants to choose!

Irrespective of whether you 4-4-5 or any other method of weekly-based reporting, there will be a requirement to insert a 53rd week from time to time. This comes about because of (a) 52 weeks of 7 days is one day short of a normal year of 365 days and (b) leap years add a further day every (nearly) four years too. So after 6 years, you'll have had an extra 6 days from (a) and, likely, an extra 1 day from (b), and you'll need to cater for a 53rd week. Depending on timing (ie where the leap years are - or aren't!) this may come after the fifth year or may be delayed until the seventh - but every sixth year is what you should plan for. If your business still wants to report over 12 periods, then one of the existing periods will need to have the extra week added.

But even if you do have to have two (or more) sets of cycles (for instance, if you are doing reporting for business with different year start/end dates) its easy to extend a calendar table to contain multiple sets of fiscal period data by having distinct sets of columns for each set of cycles.
Post #795606
« Prev Topic | Next Topic »

Add to briefcase ««1234»»»

Permissions Expand / Collapse