T-SQL date pickup - sql

I am looking for some T-SQL code that should pick the date which is "One Year back from current date (at the same time last Sunday in the month of January)".
I have some T-SQL code which is being used in SQL Server 2014:
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4), GetDate(), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)
The above code picks the date for current year's (last Sunday in January month). But I want T-SQL code to pick last year's (last Sunday's date in January month) date.
In detail - I want T-SQL code to produce expected result from below table
Current day Expected result
---------------------------------------
2017-02-05 2016-01-31
2017-01-05 2015-01-25
2018-02-19 2017-01-29
2018-01-19 2016-01-31
2019-02-28 2018-01-28
Please note always year starts from "Last Sunday in January month".

There will be more concise solutions, but when we assume that your code is time-tested and robust, I would simply substitute the GETDATE() by an expression that is now minus one year:
DATEADD(year, -1, GETDATE())
Thus:
SELECT
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(DATEADD(year,-1,GetDate())) = 1 THEN CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) - 1 ELSE CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)

If you have a calendar table, you can skip the first CTE (and probably the MAXRECURSION option) and just use the calendar table. Hopefully this is clearly correct:
declare #today date
set #today = CURRENT_TIMESTAMP
;With Dates as (
select CONVERT(date,'19000101') as d
union all
select DATEADD(day,1,d) from Dates where d < '21000101'
), ApplicableSundays as (
select d,ROW_NUMBER() OVER (ORDER BY d desc) as rn
from Dates
where d < #today and
DATEPART(month,d) = 1 and
DATEPART(weekday,d) = DATEPART(weekday,'20150913') and --Any known Sunday
DATEPART(day,d) between 25 and 31
)
select d
from ApplicableSundays where rn = 2
option (maxrecursion 0)
Dates generates all dates in the 20th and 21st centuries, which is hopefully flexible enough for your purposes.
ApplicableSundays filters these rows down to dates which occur before #today, are in January, are a Sunday (using a known-good date rather than relying on any particular DATEFIRST setting) and falls between the 25th and the 31st of that month.
We then pick the second most recent of these dates, which must be the start of last year, if our years start on the last Sunday of each January.
If you're working against a table full of dates for which you wish to find this "start of last year" value, you would introduce it as a join in the ApplicableDates CTE and partition the ROW_NUMBER() aggregate using those values, so that you can find all of the Sundays in parallel.

This will seclude the columns with the data .
Please give it a try let me know. Thanks.
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4),
GetDate(), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120) AS CurrentDay , convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(DATEADD(year,-1,GetDate())) = 1 THEN CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) - 1 ELSE CONVERT(VARCHAR(4),
DATEADD(year,-1,GetDate()), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120) as ExpectedResult

Related

Conversion failed when converting the varchar value '2 year -2 month 21 day' to data type int

I am trying to find out for how long employees have been working for the company and I should also find out if any of the employees have a resignation date.
But I don't have data for this right now, therefore value of this column in my database is Null. But i consider in my sql when it's happened take Resignation Date instead current time But for now take work start date and current time. my theory is if the column resignation date is null, take "Current Date - Work start date", else
should it take resignation date - Work start date.
I wrote a SQL query for this theory and it's working fine but when I tried return for ex year month day for how long employees working I got a problem which is:
a ) I struggled with this error for hours and I also tried convert those lines, I thought its should be converted but it's not working well so, when I run my main query, I get this error:
> Msg 245, Level 16, State 1, Line 1
> Conversion failed when converting the varchar value '2 year -2 month 21 day' to data type int.
My main query looks like this:
**--- sql for how long employees work with year month day ----**
CASE
WHEN Users.ResignationDate IS NOT NULL
THEN DATEDIFF(YEAR, WorkStartDate, ResignationDate)
ELSE CAST(DATEDIFF(yy, WorkStartDate, GETDATE()) AS varchar(4)) + ' year ' +
CAST(DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, WorkStartDate, GETDATE()), WorkStartDate), GETDATE()) AS varchar(2)) + ' month ' +
CAST(DATEDIFF(dd, DATEADD(mm, DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, WorkStartDate, GETDATE()), WorkStartDate), GETDATE()), DATEADD(yy, DATEDIFF(yy, WorkStartDate, GETDATE()), WorkStartDate)), GETDATE()) AS varchar(2)) +' day' END as Ancinitet,
Paychecks.DepartmentName AS Afdelinger
FROM dbo.Paychecks INNER JOIN dbo.Users ON Users.Id=Paychecks.UserId
WHERE Users.CustomerId=214 order by Users.FirstName;
A case expression returns a single value -- and a single type. Yours is returning two different types, and SQL Server decides that the appropriate return type is the first one, an integer.
Just cast to a string:
(CASE WHEN Users.ResignationDate IS NOT NULL
THEN CAST(DATEDIFF(YEAR, WorkStartDate, ResignationDate) as VARCHAR(255))
ELSE (CAST(DATEDIFF(year, WorkStartDate, GETDATE()) AS varchar(4)) +' year ' +
CAST(DATEDIFF(month, DATEADD(year, DATEDIFF(year, WorkStartDate, GETDATE()), WorkStartDate), GETDATE()) AS varchar(2)) +' month ' +
CAST(DATEDIFF(day, DATEADD(month, DATEDIFF(month, DATEADD(year, DATEDIFF(year, WorkStartDate, GETDATE()), WorkStartDate), GETDATE()), DATEADD(year, DATEDIFF(yy, WorkStartDate, GETDATE()), WorkStartDate)), GETDATE()) AS varchar(2)) +' day'
)
END) as Ancinitet,
I suspect there is a better way to do what you want. If that is of interest, ask another question with sample data and desired results.
What's wrong with this?
datediff(year, workStartDate, isNull(resignationDate, getDate()))
You'd be better off using weeks instead of months, since a week always contains 7 days. You could also use a 30 day month which will generally get you close but your days may be off by a couple.
DECLARE #StartDate DATETIME = '20151115',
#EndDate DATETIME = GETDATE();
SELECT YearWeekDay = CONCAT(DATEDIFF(DAY, #StartDate, #EndDate) / 365, ' year ', DATEDIFF(DAY, #StartDate, #EndDate) % 365 / 7, ' week ',
DATEDIFF(DAY, #StartDate, #EndDate) % 365 % 7, ' day'
),
YearMonthDay = CONCAT(DATEDIFF(DAY, #StartDate, #EndDate) / 365, ' year ', DATEDIFF(DAY, #StartDate, #EndDate) % 365 / 30, ' month ',
DATEDIFF(DAY, #StartDate, #EndDate) % 365 % 30, ' day '
),
YWDHMS = CONCAT( DATEDIFF(SECOND, #StartDate, #EndDate) / 86400 / 365, 'y ', DATEDIFF(SECOND, #StartDate, #EndDate) / 86400 % 365 / 7, 'w ',
DATEDIFF(SECOND, #StartDate, #EndDate) / 86400 % 365 % 7, 'd ', DATEDIFF(SECOND, #StartDate, #EndDate) % 86400 / 3600, 'h ',
DATEDIFF(SECOND, #StartDate, #EndDate) % 3600 / 60, 'm ', DATEDIFF(SECOND, #StartDate, #EndDate) % 60, 's '
);
Output:
StartDate EndDate YearWeekDay YearMonthDay YWDHMS
2015-11-15 2017-02-22 15:11:34.123 1 year 14 week 2 day 1 year 3 month 10 day 1y 14w 2d 15h 11m 34s

Pulling a month of rows in SQL Server based on a dynamic day

I have a table in SQL server that looks like this (there are other fields, but there are the only 2 I care about):
-----------------------------------------
| DraftDay (int)| MonthlyFee (money)|
-----------------------------------------
1 18.75
2 15.25
2 15.25
3 15.25
3 28.12
4 15.25
4 3.75
4 18.19
5 12.75
6 13.80
6 14.25
... ...
... ...
What I am trying to do is write a query that will group by DraftDay and sum the MonthlyFee field. Simple enough. But the hard part is, I need to get the date of one month ago, plus one day and pull the days from that day to the current day. For example, if today is 4/3/2014, then I need to pull all days between 3/4/2014 and 4/3/2014. My result set should look like this:
3/4/2014 37.19
3/5/2014 12.75
3/6/2014 28.05
... ...
... ...
4/1/2014 18.75
4/2/2014 30.50
4/3/2014 43.37
I have created this query:
SELECT
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME) AS DraftDate,
SUM(MonthlyPayment) AS MonthlyPayment FROM dbo.Advertiser
WHERE IsDeleted=0
AND IsPaidInFull=0
AND IsAdvertiserActive=1
AND EftDraftDay>0
AND (CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)) >=
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)
GROUP BY
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)
Because today's date is 4/17/2014, I should be getting results back from 3/18/2014 thru 4/17/2014. The results I'm getting back are only from 3/18/2014 thru 3/28/2014. How can I also add 4/1/2014 thru 4/17/2014 into my result set? I thought about a UNION, but those don't seem to work well with GROUP BY's.
After trying to figure this out and looking at my query, I think I am totally over engineering this. My query looks so complex for such a simple thing. Can anyone please help me out with this?
Try this:
declare #lastmonth date = convert(date,dateadd(day,1,dateadd(month,-1,getdate())))
declare #thismonth date = convert(date,getdate())
select
case
when draftday < datepart(day,#thismonth)
then convert(date, cast(datepart(yyyy,#thismonth) as varchar) + right('00'+cast(datepart(MM,#thismonth) as varchar),2) + right('00'+cast(cast(draftday as varchar) as varchar),2))
else convert(date, cast(datepart(yyyy,#lastmonth) as varchar) + right('00'+cast(datepart(MM,#lastmonth) as varchar),2) + right('00'+cast(cast(draftday as varchar) as varchar),2))
end,
sum(monthlyfee) from tbl
group by draftday
This will treat days before current day as belonging to current month, and others as from last month, and then sum up. Please let me know if this is doing what you expect.
I'd use the Calendar table (http://www.dbdelta.com/calendar-table-and-datetime-functions/), something like this:
declare #d datetime ='20140403'
select c.CalendarDate, sum(#t.MonthlyFee)
from calendar c
left join #t on c.CalendarDay = #t.DraftDay
where CalendarDate between dateadd(month, -1, #d)+1 and #d
group by c.CalendarDate

Get last Friday's Date unless today is Friday using T-SQL

I'm trying to get the correct SQL code to obtain last Friday's date. A few days ago, I thought I had my code correct. But just noticed that it's getting last week's Friday date, not the last Friday. The day I'm writing this question is Saturday, 8/11/2012 # 12:23am. In SQL Server, this code is returning Friday, 8/3/2012. However, I want this to return Friday, 8/10/2012 instead. How can I fix this code? Since we're getting to specifics here, if the current day is Friday, then I want to return today's date. So if it were yesterday (8/10/2012) and I ran this code yesterday, then I would want this code to return 8/10/2012, not 8/3/2012.
SELECT DATEADD(DAY, -3, DATEADD(WEEK, DATEDIFF(WEEK, 0, GETDATE()), 0))
try this:
declare #date datetime;
set #date='2012-08-09'
SELECT case when datepart(weekday, #date) >5 then
DATEADD(DAY, +4, DATEADD(WEEK, DATEDIFF(WEEK, 0, #date), 0))
else DATEADD(DAY, -3, DATEADD(WEEK, DATEDIFF(WEEK, 0, #date), 0)) end
result:
2012-08-03
Example2:
declare #date datetime;
set #date='2012-08-10'
SELECT case when datepart(weekday, #date) >5 then
DATEADD(DAY, +4, DATEADD(WEEK, DATEDIFF(WEEK, 0, #date), 0))
else DATEADD(DAY, -3, DATEADD(WEEK, DATEDIFF(WEEK, 0, #date), 0)) end
result:
2012-08-10
Modular arithmetic is the most direct approach, and order of operations decides how Fridays are treated:
DECLARE #test_date DATETIME = '2012-09-28'
SELECT DATEADD(d,-1-(DATEPART(dw,#test_date) % 7),#test_date) AS Last_Friday
,DATEADD(d,-(DATEPART(dw,#test_date+1) % 7),#test_date) AS This_Friday
Use this :
SELECT DATEADD(day, (DATEDIFF (day, '19800104', CURRENT_TIMESTAMP) / 7) * 7, '19800104') as Last_Friday
None of that? Try this:
DECLARE #D DATE = GETDATE()
SELECT DATEADD(D,-(DATEPART(W,#D)+1)%7,#D)
A tested function which works no matter what ##DATEFIRST is set to.
-- ==============
-- fn_Get_Week_Ending_forDate
-- Author: Shawn C. Teague
-- Create date: 2017
-- Modified date:
-- Description: Returns the Week Ending Date on DayOfWeek for a given stop date
-- Parameters: DayOfWeek varchar(10) i.e. Monday,Tues,Wed,Friday,Sat,Su,1-7
-- DateInWeek DATE
-- ==============
CREATE FUNCTION [dbo].[fn_Get_Week_Ending_forDate] (
#DayOfWeek VARCHAR(10),#DateInWeek DATE)
RETURNS DATE
AS
BEGIN
DECLARE #End_Date DATE
,#DoW TINYINT
SET #DoW = CASE WHEN ISNUMERIC(#DayOfWeek) = 1
THEN CAST(#DayOfWeek AS TINYINT)
WHEN #DayOfWeek like 'Su%' THEN 1
WHEN #DayOfWeek like 'M%' THEN 2
WHEN #DayOfWeek like 'Tu%' THEN 3
WHEN #DayOfWeek like 'W%' THEN 4
WHEN #DayOfWeek like 'Th%' THEN 5
WHEN #DayOfWeek like 'F%' THEN 6
ELSE 7
END
select #End_Date =
CAST(DATEADD(DAY,
CASE WHEN (#DoW - (((##datefirst) + datepart(weekday, #DateInWeek)) % 7)) = 7
THEN 0
WHEN (#DoW - (((##datefirst) + datepart(weekday, #DateInWeek)) % 7)) < 0
THEN 7 - ABS(#DoW - (((##datefirst) + datepart(weekday, #DateInWeek)) % 7))
ELSE (#DoW - (((##datefirst) + datepart(weekday, #DateInWeek)) % 7) )
END
,#DateInWeek) AS DATE)
RETURN #End_Date
END
This will give you the Friday of Last week.
SELECT DATEADD(day, -3 - (DATEPART(dw, GETDATE()) + ##DATEFIRST - 2) % 7, GETDATE()) AS LastWeekFriday
This will give you last Friday's Date.
SELECT DATEADD(day, +4 - (DATEPART(dw, GETDATE()) + ##DATEFIRST-2) % 7, GETDATE()) AS LastFriday
select convert(varchar(10),dateadd(d, -((datepart(weekday, getdate()) + 1 + ##DATEFIRST) % 7), getdate()),101)
Following code can be use to return any last day by replacing #dw_wk, test case below use friday as asked in original questions
DECLARE #date SMALLDATETIME
,#dw_wk INT --last day of week required - its integer representation
,#dw_day int --current day integer reprsentation
SELECT #date='8/11/2012'
SELECT #dw_day=DATEPART(dw,#date)
SELECT #dw_wk=DATEPART(dw,'1/2/2015') --Just trying not to hard code 5 for friday, here we can substitute with any date which is friday
SELECT case when #dw_day<#dw_wk then DATEADD(DAY, #dw_wk-7-#dw_day,#date) else DATEADD(DAY,#dw_wk-#dw_day, #date) END
Here's an answer I found here adapted from MySQL to T-SQL that is a one liner using all basic arithmetic (no division or modulos):
SELECT DATEADD(d, 1 - datepart(weekday, dateadd(d, 2, GETDATE())), GETDATE())
You can do all sorts of combinations of this, like get next Friday's date unless today is Friday, or get last Thursday's date unless today is Thursday by just changing the 1 and the 2 literals in the command:
Get next Friday's date unless today is Friday
SELECT DATEADD(d, 7 - datepart(weekday, dateadd(d, 1, GETDATE())), GETDATE())
Get last Thursday's date unless today is Thursday
SELECT DATEADD(d, 1 - datepart(weekday, dateadd(d, 3, GETDATE())), GETDATE())
I have had this same issue, and created the following example to show how to do this and to make it flexible to use whichever day of the week you want. I have different lines in the SELECT statement, just to show what this is doing, but you just need the [Results] line to get the answer. I also used variables for the current date and the target day of the week, to make it easier to see what needs to change.
Finally, there is an example of results when you want to include the current date as a possible example or when you always want to go back to the previous week.
DECLARE #GetDate AS DATETIME = GETDATE();
DECLARE #Target INT = 6 -- 6 = Friday
SELECT
#GetDate AS [Current Date] ,
DATEPART(dw, #GetDate) AS [Current Day of Week],
#Target AS [Target Day of Week] ,
IIF(#Target = DATEPART(dw, #GetDate), 'Yes' , 'No') AS [IsMatch] ,
IIF(#Target = DATEPART(dw, #GetDate), 0 , ((7 + #Target - DATEPART(dw, #GetDate)) % 7) - 7) AS [DateAdjust] ,
------------------------------------------------------------------------------------------------------------------------------------------------
CAST(IIF(#Target = DATEPART(dw, #GetDate), #GetDate, DATEADD(d, (((7 + #Target - DATEPART(dw, #GetDate)) % 7) - 7), #GetDate)) AS DATE) AS [Result]
------------------------------------------------------------------------------------------------------------------------------------------------
;
SELECT
#GetDate AS [Current Date] ,
DATEPART(dw, #GetDate) AS [Current Day of Week],
#Target AS [Target Day of Week] ,
((7 + #Target - DATEPART(dw, #GetDate)) % 7) - 7 AS [DateAdjust] ,
------------------------------------------------------------------------------------------------------------------------------------------------
CAST(DATEADD(d, (((7 + #Target - DATEPART(dw, #GetDate)) % 7) - 7), #GetDate) AS DATE) AS [NOTIncludeCurrent]
------------------------------------------------------------------------------------------------------------------------------------------------
;
SELECT DECODE(TO_CHAR(SYSDATE,'DY'),'FRI',SYSDATE,NEXT_DAY(SYSDATE, 'FRI')-7) FROM dual;

T-SQL select daily totals BUT daily cut off is not midnight

I need to count number of sales, and sum the totals of sales by date, easy. But the curve pitch is - I need the "cut off" to be 6pm not midnight.
6pm the day before until 6pm day of.
What's throwing me is "grouping". My counts are only pulling only the true date not "6pm" info.
Sort30 Day30 Total Counter
-------- ---------- --------------------- -----------
20120810 08/10/2012 675.32 9
20120809 08/09/2012 1314.68 16
Query:
SELECT top 30 CONVERT(VARCHAR(8), chickendate, 112) AS varSort30,
CONVERT(VARCHAR(10), chickendate, 101) AS varDay30,
SUM(CAST(transAmount AS money)) AS varTotal,
Count(chickendate) AS varCounter
FROM CHICKEN
WHERE
(chickendate >= dateadd(hour, 18, dateadd(day, datediff(day, 0, chickendate), -1)) AND
chickendate < dateadd(hour, 18, dateadd(day, datediff(day, 0, chickendate), 0)))
GROUP BY CONVERT(VARCHAR(8), chickendate, 112),
CONVERT(VARCHAR(10), chickendate, 101)
ORDER BY CONVERT(VARCHAR(8), chickendate, 112) DESC
Going round and round, I feel its something staring me in the face. Thanks.
If my logic is correct, this should give you the correct results:
SELECT TOP 30
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30,
CONVERT(VARCHAR(10), modifiedChickenDate, 101) AS varDay30,
SUM(CAST(transAmount AS money)) AS varTotal,
COUNT(modifiedChickenDate) AS varCounter
FROM (
SELECT
transAmount,
DATEADD(HOUR, 6, chickendate) AS modifiedChickenDate
FROM CHICKEN
) sub
GROUP BY
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30,
CONVERT(VARCHAR(10), modifiedChickenDate, 101) AS varDay30,
ORDER BY
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30
If chickendate is a DATETIME instead of just a DATE, then you can use
GROUP BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101)
to advance the date 6 hours (making your cutoff 6 hours earlier than midnight, or 6pm), then group on the Day of year. This only works if you're storing time information, which I'm not sure you are. Post some schema for the necessary tables. But I think you're looking for...
SELECT TOP 30 CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101) as Date
, SUM(CAST(transAmount as money)) AS Total
, Count(*) as Counter
FROM Chicken
GROUP BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101)
ORDER BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101) DESC
How about:
GROUP BY CONVERT(VARCHAR(8), dateadd(hour, 6, chickendate), 112)
So '2012-08-09 18:00:00' is grouped by 20120810, and '2012-08-09 17:59:59' is grouped by 20120809.

SQL Date Function

I may be using the wrong term (hence why I can't find it on google).
"Are there any functions or common code for Accounting Months Deliminations?"
For Example, this month started on a friday but on most accounting journals the weeks are measured by the first monday of the month so instead of having the 1st of July it would be the 4th of July. Same thing with the month end (29th instead of the 31st)
Again, I'm sure someone has created this 'wheel' before, and I can't seem to find it for the life of me.
The following query assumes a table, SalesTable, has a field called Amount (the value you want to sum) and a field called SaleDate (the date on which the sale occured.) It also assumes that accounting months begin the first Monday of the month and end on the Sunday prior to the beginning of the next accounting month.
Again, I highly recommend a table-based approach to this, but if you can't modify the schema, this should do the trick in T-SQL:
SELECT
CASE WHEN s.SaleDate < DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
THEN DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, DATEADD(day,-7,s.SaleDate) ),DATEADD(day,-7,s.SaleDate) )), 0)
ELSE DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
END AccountingMonth,
SUM(s.Amount) TotalSales
FROM SalesTable s
GROUP BY
CASE WHEN s.SaleDate < DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
THEN DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, DATEADD(day,-7,s.SaleDate) ),DATEADD(day,-7,s.SaleDate) )), 0)
ELSE DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
END
Note that the AccountingMonth return field actually contains the date of the first Monday of the month. In actual practice, you probably want to wrap this entire query in another query that reformats AccountingMonth to whatever you like... "2011-07", "2011-08", etc.
Here's how it works: This bit of code is the important part:
DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
It takes any date and returns the first Monday of the month in which that date occurred. In your case, however, you have to do a little more work because a sale might have occurred in the window between the first of the month and the first Monday of the month. The CASE statement detects that scenario and, if it's true, subtracts a week off of the date before calculating the first Monday.
Good luck!
-Michael
I have some code that takes in a year and month and returns the fiscal start and end dates. Perhaps this will give you something to go by:
DECLARE #yr int;
DECLARE #mo int;
SELECT #yr = 2011
SELECT #mo = 7
DECLARE #FiscalMonthStartDate datetime
DECLARE #FiscalMonthEndDate datetime
DECLARE #startOfMonth datetime
DECLARE #startOfNextMonth datetime
select #startOfMonth = CAST((CAST(#yr AS VARCHAR(4)) + '-' + CAST(#mo AS VARCHAR(2)) + '-' + '01') as DATE)
select #startOfNextMonth = CAST((CAST(#yr AS VARCHAR(4)) + '-' + CAST((#mo + 1) AS VARCHAR(2)) + '-' + '01') as DATE)
SELECT #FiscalMonthStartDate =
CASE
WHEN DATEPART(DW,#startOfMonth) = 0
THEN DATEADD(DD, 1, #startOfMonth)
ELSE
DATEADD(DD, 8 - DATEPART(DW,#startOfMonth), #startOfMonth)
END
SELECT #FiscalMonthEndDate =
CASE
WHEN DATEPART(DW,#startOfNextMonth) = 0
THEN DATEADD(DD, 1, #startOfNextMonth)
ELSE
DATEADD(DD, 8 - DATEPART(DW,#startOfNextMonth), #startOfNextMonth)
END
-- subtract one day to get end of fiscal month (not start of next fiscal month)
SELECT #FiscalMonthEndDate = DATEADD(DD, -1, #FiscalMonthEndDate)
SELECT #FiscalMonthStartDate, #FiscalMonthEndDate