T-SQL - receive date value and adjust day / month retaining year - tsql

I am trying to perform a little date manipulation on a value I will be passing in to a stored procedure.
Example Inputs;
31/12/2008
15/11/2007
21/05/2005
Expected Output;
31/12/2012
15/11/2012
21/05/2012
Formatted code from the answer provided;
DECLARE #date DATETIME = '31/12/2007'
DECLARE #year INT
SET #year = DATEPART(YEAR, GETDATE())
SELECT DATEADD(YEAR, #year - DATEPART(YEAR, #date), #date)

You are trying to convert the year component to 2012?
-- get the year part
DATEPART(YEAR, [Date])
-- get the number of years to add
2012 - DATEPART(YEAR, [Date])
-- add that many years to the date
DATEADD(YEAR, 2012 - DATEPART(YEAR, [Date]), [Date])

Related

Executing Stored Procedure With Dynamic Date Parameter - Conversion Error

I'm trying to execute the following stored procedure with a dynamic date parameter so that it picks up 6 months ago to yesterday.
When I run on a server with US language settings it works however when I run on a server with UK language settings I get the error:
Conversion failed when converting date and/or time from character string
I have tried converting the date to YYYYMMDD as this works on all servers however I have been unable to - I have even tried to force into a VARCHAR but no luck :(
Is there a way to get the date as YYYYMMDD format? Hope you can help.
USE Reports
DECLARE #DteStart DATETIME2(3)
DECLARE #DteEnd DATETIME2(3)
SELECT #DteStart = DATEADD(MM, DATEDIFF(MM, 0, CONVERT(DATE,GETDATE(),103))-6,0)
SELECT #DteEnd = DATEADD(DD, DATEDIFF(DD, 0, CONVERT(DATE,GETDATE(),103))-1,0)
EXEC [dbo].[spReportsMyData] #DteStart, #DteEnd;
Try something like this:
Declare
#StartDate as Date
, #EndDate as Date
Set #EndDate = DateAdd(Day, -1, Getdate())
Set #StartDate = DateAdd(Month, -6, #EndDate)
Select
Convert(VARCHAR(10), #StartDate, 112) As StartDate
, CONVERT(VARCHAR(10), #EndDate, 112) As EndDate
Does your stored procedure seriously need the dates specifically in YYYYMMDD format, though?

Find Number Of days and Months and years between 2 Dates

I am trying to Get Years, months and no. of days between 2 dates.
But when dates are like below, it gives wrong output (Month part of FromDate is greater than Todate).
declare #FromDate date='2010-10-27'
declare #Todate date='2012-03-02'
SELECT
DATEDIFF( mm, #FromDate, #ToDate) / 12 AS years
, datediff(mm,#FromDate, #ToDate) % 12 AS months
, DATEDIFF( dd, DATEADD( mm, DATEDIFF( mm, #FromDate, #ToDate), #FromDate), #ToDate) as Days
**It Shows Output as**
Years Months days
1 5 -25
It should be 4 months and 29 days. Please tell me how can I get desired Output.
Thanks in Advance
The following might solve the problem - but I think, the correct values would be 1 year, 4 month and 4 days!?
declare #start date = '2010-10-27'
declare #ende date = '2012-03-02'
SELECT DATEDIFF(mm, #start, #ende)/12 MyYears
,(DATEDIFF(mm, #start, #ende)%12)-1 MyMonths
,DATEDIFF(dd, DATEADD(mm, (DATEDIFF(mm, #start, #ende)%12)-1,DATEADD(yy, DATEDIFF(mm, #start, #ende)/12, #Start)), #Ende) MyDays
--Check of result
SELECT dateadd(dd, 4, dateadd(mm, 4, dateadd(yy, 1, #start))) x
I used this. This gives the desired result.
(#FromDate date,
#ToDate Date
)
DECLARE #Years INT, #Months INT, #Days INT
SET #Years = DATEDIFF(YEAR, #FromDate, #ToDate)
IF DATEADD(YY, #Years, #FromDate) > #ToDate
BEGIN
SET #Years = #Years - 1
END
SET #FromDate = DATEADD(YY, #Years, #FromDate)
SET #Months = DATEDIFF(MM, #FromDate, #ToDate)
IF DATEADD(MM, #Months, #FromDate) > #ToDate
BEGIN
SET #Months = #Months - 1
END
SET #FromDate = DATEADD(MM, #Months, #FromDate)
SET #Days = DATEDIFF(DD, #FromDate, #ToDate)
Select #Years as Years,#Months as Months,
#Days as Days
Maybe this is Correct
declare #FromDate date='2010-10-27'
declare #Todate date='2012-03-02'
SELECT
DATEDIFF( yy,#FromDate,#Todate) AS YEARS,
DATEDIFF( mm,#FromDate,#Todate) AS MONTHS,
DATEDIFF( dd,#FromDate,#Todate) AS DAYS

Get a list of mondays between date range

I need to show N records with each record being 1 weeks worth of summarized data. One of the inputs will be a date range.
Since each week should start with monday, I want to get a list of all the monday dates in the date range. Any suggestions to get that?
Note: This has to run on SQL 2005
declare #dt datetime
SET #dt = '2010-01-01'
declare #dtEnd datetime
SET #dtEnd = '2010-12-04'
DECLARE #day AS NVARCHAR(30)
WHILE (#dt < #dtEnd) BEGIN
-- insert into table(datefield)
-- values(#dt)
SET #day = UPPER(DATENAME(weekday,#dt))
IF #day = 'MONDAY'
--PRINT 'date is: ' + CAST(#dt AS VARCHAR(30))
BEGIN
PRINT 'date is: ' + CAST(#dt AS VARCHAR(30)) + ' ' + #day
END
SET #dt = DATEADD(day, 1, #dt)
END
The code you show seems to work, but it can be made more efficient. The code I show below will calculate the first Monday on or after the start date. Then it is used in the same loop but we simply add 7 days each time through the loop.
declare #dt datetime,
#dtEnd datetime
SET #dt = '2010-01-01'
SET #dtEnd = '2010-12-04'
Set #dt = DateAdd(Day, 7 - DateDiff(Day, 0, #dt) % 7, #dt)
WHILE (#dt < #dtEnd) BEGIN
PRINT 'date is: ' + CAST(#dt AS VARCHAR(30)) + ' ' + DateName(Weekday, #dt)
SET #dt = DATEADD(day, 7, #dt)
END
One thing I notice about your code is that you are using DateName, which works ok for displaying the weekday name, but not so good for making decisions (like you are doing). The problem with DateName is that it uses the language setting of the currently logged in user.
The following code shows this behavior.
set language 'spanish'
Select DateName(Weekday, GetDate())
set language 'us_english'
Select DateName(Weekday, GetDate())
Basically, if the language of a user was set to spanish, your code would fail but mine would not. Mine works because it uses the fact that January 1, 1900 was a Monday. It calculates the number of days and takes a mod of that and uses that mod to add the correct number of days. Since January 1, 1900 will always be a Monday, and we are not using the DateName function, this code will work without regard to the language setting.
SELECT * from table t
WHERE t.date between 'startDate' and 'endDate'
AND (SELECT DATEPART(dw,t.StudyDate))=2

Calculate working days - Monday to Fri in Tsql

How can i calculate the last working five days which is monday to Friday. my current script gets the last monday's date, but i cannot get the last friday's date. Please help
declare #StartDate datetime
declare #EndDate datetime
--Calculate date range for report
select #EndDate = Cast(convert(char(10), getdate(), 101)+' 00:00:00' as datetime)
select #StartDate = DateAdd(d, -7, #EndDate)
select #EndDate = Cast(convert(char(10), getdate(), 101)+' 23:59:59' as datetime)
select #StartDate startdate
select #EndDate enddate
Datepart offers you an easy way to get the week day:
SET DATEFIRST 1 -- monday is first day of the week
SELECT DATEPART(weekday, '20110725')
-- result is 1
See T-SQL Date functions and SET DATEFIRST for more information.
Using the weekday, you can work out how many days ago last monday and friday are and use 'AddDate' (like you are doing now) to calculate those.
Note that you should really use DATEDIFF for this type of date range selection. If you select everything up to 23:59:59 there's always a chance that some records are left out. For example 23:59:59.001 is out of range but it's still on the same day. With DATEDIFF you can test whether it's on the same day, discarding the time part. No need to bother with casting to string, adding time and casting back.
The answer is more complex than people are assuming. What you need is to go 5 days back and find the first monday before that, and the first friday after that. You can eather use ##datefirst, a calculation or 'set firstdate 1' for that. I prefer not using the last one, because it can't be done in functions. As you can see i used the calculation, ##datefirst is just as good.
Assuming you want the last group of monday to friday that is in the past. This query will get that. If you trust your current monday, you can just add 5 days and subtract 1 minute (I wouldn't trust it, it only returns last monday if you run the query on a monday).
In my sql, I am not aiming for simplicity, I am aiming for effectivity.
DECLARE #getdate datetime = dateadd(day, cast(getdate() as int), 0)
-- the 'Declare' can also be written like this thanks to #Andriy M
--DECLARE #getdate = CAST(GETDATE() - 0.5 AS int)
SELECT #getdate - 5 - CAST(#getdate- 5 as int) % 7 monday,
dateadd(minute, -1, #getdate) - CAST(#getdate- 5 as int) % 7 friday
Result:
Monday Friday
2011-07-18 0:00:00 2011-07-22 23:59:00
*first solution was a day off #AndriyM pointed it out, it has been solved.
Answer to #Andriy M
For some reason it acted different than I expected. I can't explain it but try this
select cast(dateadd(day, cast(getdate() as int) - .5, 0) as datetime),
cast(dateadd(day, cast(getdate() as int), 0) as datetime),
cast(dateadd(day, cast(getdate() as int) + .5, 0) as datetime)
in the morning the last 2 fields has same value, in the evening the first 2 fields has the same value. I am as surprised as you are, I wish I could explain it. It was tested here
https://data.stackexchange.com/stackoverflow/query/new
These two questions:
Find last sunday
How to get last day of last week in sql?
might be of some help.
Although, if you already know how to find the last Monday, you can easily find the corresponding Friday by adding 4 days to the Monday date using the DATEADD() function. For example:
SELECT #EndDate = DATEADD(DAY, 4, #StartDate)
It's all relative to TODAY's date, right?
So find out what today is DATEPART(weekday, getdate())
And then turn that into an adjustment variable -- e.g.:
declare #adjustment int
set #adjustment = CASE DATEPART(weekday, getdate()) WHEN 'MONDAY' THEN 0 WHEN 'TUESDAY' THEN 1, ...END
So then the Monday you want would be today - 7 - #adjustment
...and the Friday you want would be Monday + 5
Declare #myMonday smalldatetime,
#myFriday smalldatetime
set #myMonday = getdate() - 7 - #adjustment
set #myFriday = #myMonday + 5
DECLARE #my int
DECLARE #myDeduct int
DECLARE #day INT
DECLARE #mydate DATETIME
SET #mydate = '2012-08-01'
SET #myDeduct = 0
SET DateFirst 1 -- Set it monday=1 (value)
--Saturday and Sunday on the first and last day of a month will Deduct 1
IF (DATEPART(weekday,(DATEADD(dd,-(DAY(#mydate)-1),#mydate))) > 5)
SET #myDeduct = #myDeduct + 1
IF (DATEPART(weekday,(DATEADD(dd,-(DAY(DATEADD(mm,1,#mydate))),DATEADD(mm,1,#mydate)))) > 5)
SET #myDeduct = #myDeduct + 1
SET #my = day(DATEADD(dd,-(DAY(DATEADD(mm,1,#mydate))),DATEADD(mm,1,#mydate)))
select (((#my/7) * 5 + (#my%7)) - #myDeduct) as Working_Day_per_month
I'll throw my hat in the ring too. :-)
DECLARE #Date datetime = '01/11/2015'
DECLARE #StartDate datetime = DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date - 6) % 7)), #Date - 6) -- MONDAY
DECLARE #EndDate datetime = DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date - 6) % 7)), #Date - 6) -- FRIDAY
SELECT '#Date' as Variable ,CONVERT(date, #Date) as DateValue ,DATENAME(dw, #Date) as DayOfTheWeek
UNION SELECT '#StartDate' as Variable ,CONVERT(date, #StartDate) as DateValue ,DATENAME(dw, #StartDate) as DayOfTheWeek
UNION SELECT '#EndDate' as Variable ,CONVERT(date, #EndDate) as DateValue ,DATENAME(dw, #EndDate) as DayOfTheWeek
-- Variable DateValue DayOfTheWeek
-- ---------- ---------- ------------
-- #Date 2015-01-11 Sunday
-- #StartDate 2015-01-05 Monday
-- #EndDate 2015-01-09 Friday
BONUS: Here you can generate a quick table of the 5 weekdays using the same technique.
SELECT DATENAME(dw, DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date - 6) % 7)), #Date - 6))) as DayOfTheWeek
, DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date - 6) % 7)), #Date - 6)) as DateValue
FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT
-- DayOfTheWeek DateValue
-- ------------------------------ -----------------------
-- Monday 2015-01-05 00:00:00.000
-- Tuesday 2015-01-06 00:00:00.000
-- Wednesday 2015-01-07 00:00:00.000
-- Thursday 2015-01-08 00:00:00.000
-- Friday 2015-01-09 00:00:00.000
Here's an explanation:
1) First we need to know what date to begin our evaluation from. For this example, we chose to use Sunday, January 11th, 2015.
DECLARE #Date2 datetime = '01/11/2015'
1b) Here's a nice to have bonus technique of getting the name for the day of the week, given a date value
SELECT #Date2 as DateValue, DATENAME(dw, #Date2) as DayOfTheWeek
2) Next we need to know deterministically (based on the US calendar) what the given day of the week is numerically between 1 and 7
NOTE: 1899.12.31 is the first Sunday before 1900.01.01, which is the MINIMUM value for the SmallDateTime data type.
NOTE: Yes, you could use DATEPART(dw, #Date) like this more simply, but it is not deterministic given that certain server environments could have different configurations
RESULTS: 1 = Sunday | 2 = Monday | 3 = Tuesday | 4 = Wednesday | 5 = Thursday | 6 = Friday | 7 = Saturday
SELECT ((DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date2) % 7) + 1) [DayOfWeek Deterministic (Based on US)]
3) Now, given any date, you should have a deterministic way of determining the Monday for that given week
SELECT DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date2) % 7)), #Date2) as [Monday Day of the Week - Deterministic (Based on US)]
4) Now, given any date, you should have a deterministic way of determining the Friday for that given week
SELECT DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), #Date2) % 7)), #Date2) as [Monday Day of the Week - Deterministic (Based on US)]
5) The last date manipulation technique we need is to know how to get into a week that has the first full week of weekdays happening before it. For instance, if we are on a Sunday and we subtract 1 day from it, then we get to Saturday, which puts us in the previous week, which is the first full week of weekdays. Alternatively, if we also subtracted 1 day from Monday, it would only get us to Sunday, which is not the previous week, so subtracting 1 day is not enough. On the flip side, if we were on a Saturday and subtracted 7 days, it would take us past the previous full week of weekday, into the week before it, which is too far. Here's a run down of the analysis to figure out what the magic numbers is that you can subtract by that will work with any day of the week. As you can see below, the magic number is 6.
-- DAYS TO SUBTRACT
-- Day of the Week - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7
-- =============== ==== ==== ==== ==== ==== ==== ==== ====
-- Sunday Bad Good Good Good Good Good Good Good
-- Monday Bad Bad Good Good Good Good Good Good
-- Tuesday Bad Bad Bad Good Good Good Good Good
-- Wednesday Bad Bad Bad Bad Good Good Good Good
-- Thursday Bad Bad Bad Bad Bad Good Good Good
-- Friday Bad Bad Bad Bad Bad Bad Good Good
-- Saturday Good Good Good Good Good Good Good Bad
BONUS) If you want to have all the weekdays in little table, then you would want to also use a quick zero based "tally table". There are many ways to do this, so pick your flavor. Here are few.
SELECT * FROM (SELECT 0 as DaysToAdd UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) as TT
SELECT * FROM (SELECT TOP 5 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM sys.all_columns a CROSS JOIN sys.all_columns b) as TT
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT

TSQL need to return last day of month only, how do I drop year, month & time?

I am writing a function in T-SQL returning the last day of the month regardless of the date input.
Here is my code:
Alter Function dbo.FN_Get_Last_Day_in_Month2
(#FN_InputDt Datetime)
Returns smalldatetime
as
Begin
Declare #Result smalldatetime
Set #Result =
case when #FN_InputDt <> 01-01-1900 then
DATEADD(m, DATEDIFF(M, 0,#FN_InputDt)+1, -1)
Else 0 End
Return #Result
End
The code is not working correctly, here is a test that shows the bad behavior:
SELECT dbo.fn_get_last_day_in_month (07-05-2010)
Here is the (incorrect) result:
2010-07-31 00:00:00
What is 07-05-2010...May 7th or July 5th? You need to use a safe date format, take a look at Setting a standard DateFormat for SQL Server
example from How to find the first and last days in years, months etc
DECLARE #d DATETIME
SET #d = '20100705' -- notice ISO format
SELECT
DATEADD(yy, DATEDIFF(yy, 0, #d), 0) AS FirstDayOfYear,
DATEADD(yy, DATEDIFF(yy, 0, #d)+1, -1) AS LastDayOfYear,
DATEADD(qq, DATEDIFF(qq, 0, #d), 0) AS FirstDayOfQuarter,
DATEADD(qq, DATEDIFF(qq, 0, #d)+1, -1) AS LastDayOfQuarter,
DATEADD(mm, DATEDIFF(mm, 0, #d), 0) AS FirstDayOfMonth,
DATEADD(mm, DATEDIFF(mm, 0, #d)+1, -1) AS LastDayOfMonth,
#d - DATEDIFF(dd, ##DATEFIRST - 1, #d) % 7 AS FirstDayOfWeek,
#d - DATEDIFF(dd, ##DATEFIRST - 1, #d) % 7 + 6 AS LastDayOfWeek
for just the day use day or datepart
select DAY(getdate()),
DATEPART(dd,GETDATE())
Cast the return value to a SQL datetime type, and then call the "DAY" function to get the day in as an integer. See the function reference here:
http://msdn.microsoft.com/en-us/library/ms176052.aspx
Not sure which database you're using, but this should be a standard function across all databases.
I'd return a DATETIME, I've had trouble with SMALLDATETIME in the past.
DECLARE #Result DATETIME
SET #Result = DATEADD(m , 1, #FN_Input);
RETURN CAST(FLOOR(CAST(DATEADD(d, DATEPART(d, #Result) * -1, #Result) AS FLOAT)) AS DATETIME)
Also, I think you may be a victim of SQL's complete disregard of date formatting. Always, always, always, when typing a string into test a SQL function use the following format;
'05 Jul 2010'
Your function probably works but it interpreted your date as 5th July - not 7th May.
DECLARE #date DATETIME = '20130624';
SELECT Day(EOMONTH ( #date )) AS LastDay;
GO