How can I subtract two integers datewise in SQL Server? - sql-server

I have a table which has years as int, say 202101. I want to get the difference in months with 202205.
For example 202205 - 202101 = 16 (months).
Do you know how can I do that?

Just to expand on Larnu's comment.
select datediff(month
,try_convert(date,concat(202101,'01'))
,try_convert(date,concat(202205,'01'))
)
Results
16

If you do integer division by 100 you get the year component with the remainder as the month component so another method is
DECLARE #Date1 INT =202205, #Date2 INT = 202101
SELECT (12 * (#Date1/100 - #Date2/100)) + (#Date1 % 100 - #Date2 % 100)

Related

Can any one please tell me logic behind select DATENAME(month,29*5)

select DATENAME(month,29*5)
Can any one please tell me logic behind the above query.
How it always returns correct month name when provided month number as integer.
Datetime values in Sql server are stored on 8 bytes.
The first 4 bytes represents the date and the last 4 byte represents the time.
On the date part, date is stored as the number of days since 1900-01-01.
On the time part, it's the number of clock ticks since midnight.
There are 300 clock ticks per second, so a tick is 3.33333 milliseconds.
That's also the reason why datetime is only accurate to .003 of a second.
This query will hopefully help to explain:
SELECT CAST(0 As datetime) As Date_0,
29*5 As NumberOfDays,
CAST(29*5 as datetime) As TheDate,
DATENAME(month,29*5) As TheMonthName
Results:
Date_0 NumberOfDays TheDate TheMonthName
----------------------- ------------ ----------------------- ------------
1900-01-01 00:00:00.000 145 1900-05-26 00:00:00.000 May
As for the last part of your question, 29 (28 would work as well) is the magic number here - 30 is too big (May would be returned for 4 and 5) and 27 is too small - (September would be returned for 9 and 10).
Basically i'ts just math - get the number correctly so that each time you double it with any number between 1 and 12 will give you a number of days that sums up to a day that belongs to the correct month.
You can test it yourself using this script:
DECLARE #MagicNumber int = 28
;With cte as
(
select 1 as num
union all
select num + 1
from cte
where num < 12
)
SELECT num, DATENAME(month, #MagicNumber * num ) As TheMonthName
from cte
Just change the value of #MagicNumber and see the results you get.
I think I will able to explain.
The default year-month-day for any date data type is 1900-01-01. If we consider above select query, it add 29*5 days into default date and gives the MONTHNAME.
Select DATENAME(month,29*5)
Now understand the DATENAME
DateName - Returns a character string that represents the specified datepart of the specified date. Its have different -2 argument and give the different-2 result as per datepart.
Argument 1 - Is the part of the date to return.
Argument 2 - Is a any date (Is an expression that can be resolved to a
time, date, smalldatetime, datetime, datetime2, or datetimeoffset
value.)
Here we given month as a first argument. Which means it return monthname.
The calculation of 29*5 gives 145 answer and if we simply cast into date it consider as a days and calculate as 1900-01-01 + 145 and gives the date 1900-05-26 00:00:00.000.
Means if we get the month of this will give the 5 - MAY as a answer.
Execute this query and check the answer for the above logic.
Select DATENAME(month,29*5), (29*5) , DATENAME(month, '12:10:30.123'), DATENAME(month, getdate())
select cast (145 as datetime)
DECLARE #t datetime = '12:10:30.123';
SELECT DATENAME(month, 29*5), 145/30.00;
Check for further.
MSDN Link
Convert Month Number to Month Name Function in SQL (check the #user275683 answer)
If you are simply want to show the month corresponding to month number then you should have to use like this.
declare #intMonth as int
set #intMonth = 5
Select DateName( month , DateAdd( month , #intMonth , -1 ))

Return number of months and years from int?

This seems pretty easy, but I can't seem to figure it out.
If i have a number of months, like 61
That is 5 years and 1 month.
How can I write a select to give me the value of 0105?
Presumably you're going to have a query like this:
SELECT MONTHS
FROM MY_TABLE;
You could do some division and get the number of years:
SELECT MONTHS / 12 AS YEARS
FROM MY_TABLE;
Next, you need to get the number of months remaining from that. Modulo mathematical operations are used for this:
SELECT MONTHS % 12 AS MONTHS,
MONTHS / 12 AS YEARS
FROM MY_TABLE;
Now you'll need to format it:
SELECT FORMAT(MONTHS % 12 AS MONTHS, '00'),
FORMAT(MONTHS / 12 AS YEARS, '00')
FROM MY_TABLE;
Finally, concat the two results together:
SELECT CONCAT(FORMAT(MONTHS % 12 AS MONTHS, '00'),
FORMAT(MONTHS / 12 AS YEARS, '00')) AS RESULT
FROM MY_TABLE;
you need some case , cast , /12 and %12:
declare #value int
set #value=61
select
case when len(cast(#value%12 as int))=1 then '0'+cast(#value%12 as varchar(1)) else cast(cast(#value%12 as int)as varchar(2)) end+
case when len(cast(#value/12 as int))=1 then '0'+cast(#value/12 as varchar(1)) else cast(cast(#value/12 as int)as varchar(2)) end
output: 0105
Here You have two possible solutions:
DECLARE #n INT = 61
SELECT RIGHT(REPLACE(CONVERT(varchar(8),DATEADD(Month,#n,'2000-01-01'),1),'/',''),4)
SELECT RIGHT(CAST(10000+#n%12*100+#n/12 AS VARCHAR(5)),4)

What is the most accurate way of using DATEDIFF in SQL Server?

I have two computed columns (MonthsInService and YearsInService) with the following expressions.
MonthsInService = (datediff(month,[DateEngaged],getdate()))
YearsInService = (datediff(month,[DateEngaged],getdate())/(12))
Now if for example DateEngaged = 2012-April-09 and getdate() is 2013-April-08, MonthsInService returns 12 and YearsInService is 1.
My application requires that YearsInService be Zero since there is still one day to go before the employees first Anniversary.
Am not even sure how to best handle the MonthsInService column since months have varying number of days.
Unfortunately, DATEDIFF computes the number of transitions of the element, rather than the usual, human intuition of the difference between two dates (e.g. DATEDIFF(year,'20121231','20130101') is 1, even though not many people would say that there's a difference of a year).
The solution I'd use is a bit repetitive, but doesn't need a separate function, and always gets e.g. leap years correct:
declare #T table (
DateEngaged datetime not null,
MonthsInService as CASE
WHEN DATEADD(month,DATEDIFF(month,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
THEN DATEDIFF(month,DateEngaged,GETDATE()) - 1
ELSE DATEDIFF(month,DateEngaged,GETDATE())
END,
YearsInService as CASE
WHEN DATEADD(year,DATEDIFF(year,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
THEN DATEDIFF(year,DateEngaged,GETDATE()) - 1
ELSE DATEDIFF(year,DateEngaged,GETDATE())
END
)
insert into #T (DateEngaged) values ('20120409'),('20120408')
select * from #T
Produces:
DateEngaged MonthsInService YearsInService
----------------------- --------------- --------------
2012-04-09 00:00:00.000 11 0
2012-04-08 00:00:00.000 12 1
It works by asking "If we take the naive answer produced by DATEDIFF, does it given an answer that's too high by 1?" - and if so, we just subtract one from the answer it gives. DATEDIFF should only ever be over by 1.
Via using day you can reach the result:
select
datediff(month,'2012-April-09','2013-April-08') MonthsInService
,datediff(day,'2012-April-09','2013-April-08')/365 YearsInService
Output:
12 0
or use function for maximum precision:
CREATE FUNCTION [dbo].[getFullYears]
(
#dateX datetime,
#dateY datetime
)
RETURNS int
AS
BEGIN
DECLARE #y int
SET #y =DATEDIFF(year,#dateX,#dateY)
IF (#dateY < DATEADD(year, #y, #dateX)) SET #y = #y -1
RETURN #y
END
select dbo.getFullYears('2012-April-09','2013-April-09') --1
select dbo.getFullYears('2012-April-09','2013-April-08') --0
For months calculation you can refer here: Calculating number of full months between two dates in SQL
Try this query :
DATEDIFF(DAY, CONVERT(date, dtmDOB),
CONVERT(date, GETDATE()))*(12.0/365.25)),1))
AS TotalMonths,

DATEADD with part days

I'm having a little trouble getting a count of dates in SQL SERVER. I require the number of calender days between 2 dates start and ends dates included. The problem with the example below is that it always returns 10 when I believe it should be 11.
DECLARE #FROM DATETIME, #TO DATETIME
SET #FROM = '18/12/2011 00:00:00'
SET #TO = '28/12/2011 00:00:00'
SELECT
DATEDIFF(MINUTE,#FROM,#TO), -- Returns 14459
DATEDIFF(HOUR,#FROM,#TO), -- Returns 241
DATEDIFF(DAY,#FROM,#TO), -- Returns 10
CEILING(CAST((DATEDIFF(HOUR,#FROM,#TO) / 24) as DECIMAL(9,5))) --Returns 10
CEILING(CAST(CEILING(CEILING(CAST(DATEDIFF(SECOND,#FROM,#TO) as DECIMAL(18,5))) / 60) / 60 as DECIMAL(9,5)) / 24) --Returns 10
The bottom line works if there is at least 1 second between the times but I must account for all scenarios.
My only other thought was to simply add one to the date diff to account for the part days? Is that reliable?
DATEDIFF(DAY,#FROM,#TO) + 1
I came across when answering this question How to find the total between the dates for each values
Is an expression that can be resolved to a time, date, smalldatetime,
datetime, datetime2, or datetimeoffset value. date can be an
expression, column expression, user-defined variable or string
literal. startdate is subtracted from end date.
This is taken from MSDN here.
28-18 = 10. I think you will always have to add 1 in the scenario you have because of the definition for DATEDIFF.
You need to set the #TO date to:
SET #TO = '28/12/2011 23:59:59'
To get the number of days between two dates (ignoring the time of day), including the start and end date, try;
SELECT FLOOR(CONVERT(FLOAT, #TO))-FLOOR(CONVERT(FLOAT, #FROM))+1
Edit:
SELECT DATEDIFF(d, #FROM, #TO)+1
seems to return the exact same results, which would indeed make it a more elegant way of doing it. Always thought DATEDIFF timeparts were about truncating after the calculation (which would give the wrong result if the start time was later in the day than the end time) and not truncating before the calculation which gives the correct result for your case. You learn something new every day :)
If you want a close equivalent of the C# DateTime.TotalDays() function (i.e. to know fractional days) you can use the following:
DECLARE #start DATETIME = '10 Apr 2012 15:00'
DECLARE #end DATETIME = '12 Apr 2012 16:00'
SELECT CONVERT(FLOAT, DATEDIFF(SECOND, #start, #end)) / 86400
*Note: 86400 = seconds in a day = 24 hours x 60 mins x 60 seconds

SQL Server DateTime format from automatic export script

When using the mssql script generation with data the datetime is exported with a cast:
CAST(0x00009E0E0095524F AS DateTime)
Does anyone know what format is used ?
The date in the example is shown as 2010-10-13 09:03:39.783.
Upper 4 bytes = days from 01 Jan 1900, lower 4 = time of day
It's the internal storage of datetime which is 8 bytes as two 4 byte integers, one with whole days, the other as fraction of day.
DECLARE #inttop bigint, #TheValue bigint
SET #inttop = POWER(CAST(2 AS bigint), 32)
SET #TheValue = CAST(0x00009E0E0095524F AS bigint)
SELECT
--days since 01 Jan 1900
#TheValue / #inttop,
--fractional time of day
CAST(#TheValue % #inttop AS float) / #inttop
--and confirm it
SELECT
DATEADD(DAY, #TheValue / #inttop, 0),
CAST(CAST((#TheValue % #inttop) AS float) / #inttop AS datetime)

Resources