Calculating age in a T-SQL query - sql-server

I know this has been asked but I found the following code that appears to be working correctly and just wanted to have all the pros here take a quick look at it.
I work in banking and need it to work 100% of the time and I've noticed that a lot of the queries can sometimes be incorrect. Also N.NameBirthdate is the column I am pulling from my database.
Thank you in advance.
SELECT
CASE
WHEN (MONTH(GETDATE()) * 100) + DAY(GETDATE()) >= (MONTH(N.NameBirthdate) * 100) + DAY(N.NameBirthdate)
THEN DATEDIFF(Year, N.NameBirthdate, GETDATE())
ELSE DATEDIFF(Year, N.NameBirthdate, GETDATE())-1
END AS 'Age',

Well, I'm not sure exactly if that will work 100% but are you open to other query ideas? I always use this:
SELECT FLOOR(DATEDIFF(d, N.NameBirthDate, GETDATE()) / 365.25) AS Age
if I need to calculate an age. The 365.25 is to allow for leap years.
But you may encounter slight inaccuracies if the person has not experienced a leap year and depending on the time of day the query is run. See this for more info

What you have should work. I might use a variation though just because I find it a bit more readable / obvious:
SELECT DATEDIFF(Year, N.NameBirthdate,getdate()) -
(case when month(N.NameBirthdate) > month(getdate()) or (month(N.NameBirthdate) = month(getdate()) and day(N.NameBirthdate) > day(getdate()) ) then 1 else 0 end)
To me, this makes sense because that's how you (or I do), determine someone's age. OK: what year were you born and what year is it now? What month are we in now? Is it your birth month? What day is it?
I personally don't like the leap year approach. First of all, it's not 365.25, but .24. To be precise it's 365.24219647. If you use .25 you will be wrong. Example: http://www.sqlfiddle.com/#!3/d41d8/8267

I know that this is old but I was looking around for a quick way to properly calculate someone's age and I couldn't find anything. These answers work fine but I did not want any case statement and I wanted it to be completely accurate so I developed this:
SELECT DATEPART(YY,GETDATE()-COALESCE(DOB,GETDATE()))-DATEPART(YY,0)
Ex.
DATEPART(YY,GETDATE()-COALESCE(DOB,GETDATE()))
DATEPART(YY,'07-30-2014' - '10-03-2000') = 1913...
1913 - DATEPART(YY,0)...
1913 - 1900 = 13
I like this method better because if the DOB is null then you will get 0 and no matter what the date system is, subtracting DATEPART(YY,0) will account for it. So instead of 1913 you would have 1917 - 1904 = 13 if somehow the date system was 1904(I know excel has this, does DBs??? IDK; IDC) Also, it's only 61 characters. If you DB allows for empty sets then you can use NULLIF like so:
SELECT DATEPART(YY,GETDATE()-COALESCE(NULLIF(DOB,''),GETDATE()))-DATEPART(YY,0)

SELECT DATEDIFF(yy, BirthDate, GETDATE()) - CASE
WHEN MONTH(BirthDate) > MONTH(GETDATE())
OR (MONTH(BirthDate) = MONTH(GETDATE())
AND DAY(BirthDate) > DAY(GETDATE()))
THEN 1
ELSE 0
END AS Age;

Related

Dealing with out of range value on varchar date conversion

I'm attempting to convert dates input in our system as text in the format YYYYMMDD into dates. Unfortunately our system allows the use of the 31st of any month to signify that it's the last day of the month that's important, for some functions like interest accrual etc.
I have a date showing as 20160931 which obviously fails to convert via
CONVERT(DATETIME, CONVERT(CHAR(8), [FIELD]))
and throws the out-of-range value error.
How can I overcome this, so that I can convert it to the correct value, in this case 30/09/2016?
Adapting #Shnugo's technic, I feel it's better to leave SQL to decide the end of month. Hence:
SELECT eomonth(CAST(y+m+'01' AS DATE))
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
Will give '2016-02-29' instead of '2016-02-28' with a constant '28' for Feb.
You might try something like this:
DECLARE #YourDate VARCHAR(100)='20160231';
SELECT CAST(y+m+dNew AS DATE)
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
CROSS APPLY
(
SELECT CASE WHEN CAST(m AS INT) IN(4,6,9,11) AND CAST(d AS INT)>30 THEN '30'
ELSE CASE WHEN CAST(m AS INT)=2 AND CAST(d AS INT)>28 THEN '28' ELSE d END
END AS dNew
) AS NewDay
And about a 29th of February you just have - additionally - to check if the year is to be divided by 4 :-)
UPDATE
Now it's on me to evolve #Irawan's technique :-)
Since SQL Server 2005 has not got the EOMONTH function, but it is surely better to let SQL Server do the calculation (29th of Feb implicitly solved!), I'd suggest this:
DECLARE #YourDate VARCHAR(100)='20160231';
SELECT DATEADD(SECOND,-1,DATEADD(MONTH,1,CAST(y+m+'01' AS DATETIME)))
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
This will - in any case - deliver the last second of the month...
If you want a plain DATE (without a time), you might just change the SECOND to DAY which will first jump to midnight of the first day of the next month and than go one day back...
UPDATE 2 Use existing dates if valid
Simple syntax...
DECLARE #YourDate VARCHAR(100)='20160229';
SELECT CASE WHEN ISDATE(#YourDate)=1 THEN #YourDate
ELSE DATEADD(SECOND,-1,DATEADD(MONTH,1,CAST(LEFT(#YourDate,4) + SUBSTRING(#YourDate,5,2) +'01' AS DATETIME)))
END AS CorrectDate;

How do you calculate an expiration date in SQL Server?

I need to calculate an expiration date. Should be pretty easy right? Well, my "lifespan" is in years, not days, and to make it more challenging, the years value can be fractional. Here is what I am starting with:
set #ExpirationDate = DATEADD(year, #LifeSpanYears, #BeginDate)
The problem is, DATEADD allows a decimal to be passed in, but if you read the documentation, the decimal is truncated to an int.
How do you calculate an expiration date with a fractional year?
This is the answer I came up with:
create function Reporting.CalcExpirationDate ( #LifeSpanYears decimal(9,2), #BeginDate date)
returns date
begin
return DATEADD(day, (#LifeSpanYears * 365.25), #BeginDate)
end
Then use it like this:
select Reporting.CalcExpirationDate (13 , '2015-09-01') as CalculatedDate, '2028-09-01' as ExpectedDate
union all select Reporting.CalcExpirationDate (13.5 , '2015-09-01') as CalculatedDate, '2029-03-01' as ExpectedDate
Notes: As I was working on this I realized that it is impossible to determine the exact expiration date using a fractional year as a lifespan. How do you know when to add leap days or not? If you really want the exact answer, you need lifespan in days.
In practice, the "lifespan in years" is probably a lifespan in months. If so, you might consider:
return dateadd(month, round(#LIfeSpanYears * 12, 0), #BeginDate)
This handles the issue with leap years. However, it comes at the cost of only resolving to months.

How do I find current time is between certain time period in SQL-Server

How can I ascertain from getdate() in SQL Server 2005 if the current time is between certain time period. If it is then have to set flag as one else two.
something like
getdate() timeperiod between 09:00 and 18:00 then flag =1 else flag=2
Kindly help to fix this.
According to http://msdn.microsoft.com/en-us/library/ms174420(v=sql.90).aspx
sql 2005 has datepart. I would use that. I dont know your entire query, so here is an example of how I would use it based on what I know.
select timeperiod, datepart(HH,timeperiod), case
when datepart(HH,timeperiod) >= 8 and datepart(HH,timeperiod) < 9 then 1
else 2
end as yourCase
from Visit
Although drewlander came up with a pretty good solution. I believe this is slightly better:
SELECT CASE WHEN dateadd(d,-datediff(d, 0, timeperiod), timeperiod)
BETWEEN '1900-01-01T08:00:00' and '1900-01-01T18:00:00'
THEN 1 ELSE 2 END flag
FROM <table>
Taking the day part away from the datetime making is pretty easy to compare. This can also be used for times like 08:15 to 18:03.
/* declare #MY_syncDate datetime
set #MY_syncDate=getdate()
*/
SELECT case when CAST(MY_SyncDate AS TIME) BETWEEN '9:00' and '18:00' then 1 else 0 end as flag;

SQL Date Subtraction Between Two Years

I am having an issue with a query I am trying to convert from MS Access. The query flags record for removal when it is older than 90 days but when I convert this query to sql server is is removing too many records.
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE Convert(varchar, DT.SM_T_CountTotals.PostDate, 101) <
Convert(varchar, GetDate()- 90, 101)
When I run this query in MS Access I get a total of 3793 records that are flagged but in SQL server I get 69061 records that are flagged for removal. The GetDate()-90 value is correct at 10/26/2010 but it is flagging everything from this year to be removed.
I am sure it is something easy that I am overlooking. Help please?
I figured it out:
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE DT.SM_T_CountTotals.PostDate < Convert(varchar, GetDate()- 90, 101)
You're comparing VARCHAR values, not DATEs.
101 converts to MM/DD/YY, so you're comparing month, then day, then year.
You should be using 112 (yymmdd)
Calculations between two dates can be easily done in the native data type rather than convert it to string. One can (and you have) get incorrect answers from such conversions.
Use DateDiff in the where clause to get the records that are more than 90 days old.
http://msdn.microsoft.com/en-us/library/ms189794.aspx
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE ABS (DATEDIFF (dd, Getdate(), DT.SM_T_CountTotals.PostDate)) > 90

Getting week number off a date in MS SQL Server 2005?

Is it possible to create an sql statement that selects the week number (NOT the day of week - or the day number in a week). I'm creating a view to select this extra information along with a couple of other fields and thus can not use a stored procedure. I'm aware that it's possible to create a UDF to do the trick, but if at all possible i'd rather only have to add a view to this database, than both a view and a function.
Any ideas? Also where i come from, the week starts monday and week 1 is the first week of the year with atleast 4 days.
Related:
How do I calculate the week number given a date?
Be aware that there are differences in what is regarded the correct week number, depending on the culture. Week numbers depend on a couple of assumptions that differ from country to country, see Wikipedia article on the matter. There is an ISO standard (ISO 8601) that applies to week numbers.
The SQL server integrated DATEPART() function does not necessarily do The Right Thing. SQL Server assumes day 1 of week 1 would be January 1, for many applications that's wrong.
Calculating week numbers correctly is non-trivial, and different implementations can be found on the web. For example, there's an UDF that calculates the ISO week numbers from 1930-2030, being one among many others. You'll have to check what works for you.
This one is from Books Online (though you probably want to use the one from Jonas Lincoln's answer, the BOL version seems to be incorrect):
CREATE FUNCTION ISOweek (#DATE DATETIME)
RETURNS INT
AS
BEGIN
DECLARE #ISOweek INT
SET #ISOweek = DATEPART(wk,#DATE)
+1
-DATEPART(wk,CAST(DATEPART(yy,#DATE) AS CHAR(4))+'0104')
-- Special cases: Jan 1-3 may belong to the previous year
IF (#ISOweek=0)
SET #ISOweek = dbo.ISOweek(CAST(DATEPART(yy,#DATE) - 1
AS CHAR(4))+'12'+ CAST(24+DATEPART(DAY,#DATE) AS CHAR(2)))+1
-- Special case: Dec 29-31 may belong to the next year
IF ((DATEPART(mm,#DATE)=12) AND
((DATEPART(dd,#DATE)-DATEPART(dw,#DATE))>= 28))
SET #ISOweek=1
RETURN(#ISOweek)
END
GO
You need the ISO week. From http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=60510, here's an implementation:
drop function dbo.F_ISO_WEEK_OF_YEAR
go
create function dbo.F_ISO_WEEK_OF_YEAR
(
#Date datetime
)
returns int
as
/*
Function F_ISO_WEEK_OF_YEAR returns the
ISO 8601 week of the year for the date passed.
*/
begin
declare #WeekOfYear int
select
-- Compute week of year as (days since start of year/7)+1
-- Division by 7 gives whole weeks since start of year.
-- Adding 1 starts week number at 1, instead of zero.
#WeekOfYear =
(datediff(dd,
-- Case finds start of year
case
when NextYrStart <= #date
then NextYrStart
when CurrYrStart <= #date
then CurrYrStart
else PriorYrStart
end,#date)/7)+1
from
(
select
-- First day of first week of prior year
PriorYrStart =
dateadd(dd,(datediff(dd,-53690,dateadd(yy,-1,aa.Jan4))/7)*7,-53690),
-- First day of first week of current year
CurrYrStart =
dateadd(dd,(datediff(dd,-53690,aa.Jan4)/7)*7,-53690),
-- First day of first week of next year
NextYrStart =
dateadd(dd,(datediff(dd,-53690,dateadd(yy,1,aa.Jan4))/7)*7,-53690)
from
(
select
--Find Jan 4 for the year of the input date
Jan4 =
dateadd(dd,3,dateadd(yy,datediff(yy,0,#date),0))
) aa
) a
return #WeekOfYear
end
go
Looks like the DATEPART mssql function should help you out with ...
DATEPART(wk, ‘Jan 1, xxxx’) = 1
Well I'll be.. turns out there is a way to set the first day of the week, DATEFIRST
SET DATEFIRST 1 -- for monday
Update: Now I understand better, what the OP wants.. which is custom-logic for this. I don't think MSSQL would have functions with such rich level of customization. But I may be wrong... I think you'll have to roll your own UDF here...sorry
FORGET THE OTHER ANSWERS
The question specifies "the week starts monday and week 1 is the first week of the year with atleast 4 days." This is ISO 8601 standard and what this answer provides. This function is used in production on our site.
This is all you need:
CREATE FUNCTION ISOweek (#DATE DATETIME)
RETURNS INT
AS
BEGIN
RETURN (datepart(DY, datediff(d, 0, #DATE) / 7 * 7 + 3)+6) / 7
END
GO
This will return you the week number of date entered in quotes
SELECT DATEPART( wk, 'enter the date over here' )
Looks like datepart will get you part of the way there, but you'll have to adjust to get your correct week number, based on the day of week of Jan 1 of the given year. I'm not familiar enough with T-SQL to do that, but it should be possible. Pity there isn't a mode argument as in MySQL
have you considered using the WEEK function?
This will get you the week of the year for the specified date that you pass in.
SELECT { fn WEEK(GETDATE()) } AS WeekNumber, { fn WEEK(CONVERT(DATETIME, '2008-01-01 00:00:00', 102)) } AS FirstWeekOfYear, { fn WEEK(CONVERT(DATETIME, '2008-12-31 00:00:00', 102)) } AS LastWeekOfYear
This outputs the following SQL2000 and SQL2005:
WeekNumber: 50
FirstWeekOfYear: 1
LastWeekOfYear: 53
I Hope this helps :)
Why yet again, people make mountains out of mole-hills, it astounds me?
So simple...
select DATEPART(wk, GETDATE())

Resources