SQL Select Upcoming Birthdays - sql

I'm trying to write a stored procedure to select employees who have birthdays that are upcoming.
SELECT * FROM Employees WHERE Birthday > #Today AND Birthday < #Today + #NumDays
This will not work because the birth year is part of Birthday, so if my birthday was '09-18-1983' that will not fall between '09-18-2008' and '09-25-2008'.
Is there a way to ignore the year portion of date fields and just compare month/days?
This will be run every monday morning to alert managers of birthdays upcoming, so it possibly will span new years.
Here is the working solution that I ended up creating, thanks Kogus.
SELECT * FROM Employees
WHERE Cast(DATEDIFF(dd, birthdt, getDate()) / 365.25 as int)
- Cast(DATEDIFF(dd, birthdt, futureDate) / 365.25 as int)
<> 0

Note: I've edited this to fix what I believe was a significant bug. The currently posted version works for me.
This should work after you modify the field and table names to correspond to your database.
SELECT
BRTHDATE AS BIRTHDAY
,FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()) / 365.25) AS AGE_NOW
,FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()+7) / 365.25) AS AGE_ONE_WEEK_FROM_NOW
FROM
"Database name".dbo.EMPLOYEES EMP
WHERE 1 = (FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()+7) / 365.25))
-
(FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()) / 365.25))
Basically, it gets the # of days from their birthday to now, and divides that by 365 (to avoid rounding issues that come up when you convert directly to years).
Then it gets the # of days from their birthday to a week from now, and divides that by 365 to get their age a week from now.
If their birthday is within a week, then the difference between those two values will be 1. So it returns all of those records.

In case someone is still looking for a solution in MySQL (slightly different commands), here's the query:
SELECT
name,birthday,
FLOOR(DATEDIFF(DATE(NOW()),birthday) / 365.25) AS age_now,
FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL 30 DAY),birthday) / 365.25) AS age_future
FROM user
WHERE 1 = (FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL 30 DAY),birthday) / 365.25)) - (FLOOR(DATEDIFF(DATE(NOW()),birthday) / 365.25))
ORDER BY MONTH(birthday),DAY(birthday)

Best use of datediff and dateadd. No rounding, no approximates, no 29th of february bug, nothing but date functions
ageOfThePerson = DATEDIFF(yyyy,dateOfBirth, GETDATE())
dateOfNextBirthday = DATEADD(yyyy,ageOfThePerson + 1, dateOfBirth)
daysBeforeBirthday = DATEDIFF(d,GETDATE(), dateofNextBirthday)
Thanks to #Gustavo Cardoso, new definition for the age of the person
ageOfThePerson = FLOOR(DATEDIFF(d,dateOfBirth, GETDATE())/365.25)

Liked the approach of #strelc, but his sql was a bit off. Here's an updated version that works well and is simple to use:
SELECT * FROM User
WHERE (DATEDIFF(dd, getdate(), DATEADD(yyyy,
DATEDIFF(yyyy, birthdate, getdate()) + 1, birthdate)) + 1) % 366 <= <number of days>
edit 10/2017: add single day to end

You could use the DAYOFYEAR function but be careful when you want to look for January birthdays in December. I think you'll be fine as long as the date range you're looking for doesn't span the New Year.

Sorry didn't see the requirement to neutralize the year.
select * from Employees
where DATEADD (year, DatePart(year, getdate()) - DatePart(year, Birthday), Birthday)
between convert(datetime, getdate(), 101)
and convert(datetime, DateAdd(day, 5, getdate()), 101)
This should work.

My guess is using "365.25" soon or later would be fail.
So I test the working solution using "365.25"
And It don't return the same numbers of rows for every case.
Here an example:
http://sqlfiddle.com/#!3/94c3ce/7
test with year 2016 and 2116 and you will see the difference. I only can post one link but change de /7 by /8 to see both queries. (/10 and /11 for the first answer)
So, I suggest this another query, where the point is determinate next birthday from a starting date and then compare if it is in my range of interest.
SELECT * FROM Employees
WHERE
CASE WHEN (DATEADD(yyyy,DATEDIFF(yyyy, birthdt, #fromDate),birthdt) < #fromDate )
THEN DATEADD(yyyy,DATEDIFF(yyyy, birthdt, #fromDate)+1,birthdt)
ELSE DATEADD(yyyy,DATEDIFF(yyyy, birthdt, #fromDate),birthdt) END
BETWEEN #fromDate AND #toDate

This is solution for MS SQL Server:
It returns employees with birthdays in 30 days.
SELECT * FROM rojstni_dnevi
WHERE (DATEDIFF (dd,
getdate(),
DATEADD ( yyyy,
DATEDIFF(yyyy, rDan, getdate()),
rDan)
nex )
+365) % 365 < 30

I found the solution for this. This may save someone's precious time.
select EmployeeID,DOB,dates.date from emp_tb_eob_employeepersonal
cross join dbo.GetDays(Getdate(),Getdate()+7) as dates where weekofmonthnumber>0
and month(dates.date)=month(DOB) and day(dates.date)=day(DOB)
GO
/****** Object: UserDefinedFunction [dbo].[GetDays] Script Date: 11/30/2011 13:19:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--SELECT [dbo].[GetDays] ('02/01/2011','02/28/2011')
ALTER FUNCTION [dbo].[GetDays](#startDate datetime, #endDate datetime)
RETURNS #retValue TABLE
(Days int ,Date datetime, WeekOfMonthNumber int, WeekOfMonthDescription varchar(10), DayName varchar(10))
AS
BEGIN
DECLARE #nextDay int
DECLARE #nextDate datetime
DECLARE #WeekOfMonthNum int
DECLARE #WeekOfMonthDes varchar(10)
DECLARE #DayName varchar(10)
SELECT #nextDate = #startDate, #WeekOfMonthNum = DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH,0,#startDate),0),#startDate) + 1,
#WeekOfMonthDes = CASE #WeekOfMonthNum
WHEN '1' THEN 'First'
WHEN '2' THEN 'Second'
WHEN '3' THEN 'Third'
WHEN '4' THEN 'Fourth'
WHEN '5' THEN 'Fifth'
WHEN '6' THEN 'Sixth'
END,
#DayName
= DATENAME(weekday, #startDate)
SET #nextDay=1
WHILE #nextDate <= #endDate
BEGIN
INSERT INTO #retValue values (#nextDay,#nextDate, #WeekOfMonthNum, #WeekOfMonthDes, #DayName)
SELECT #nextDay=#nextDay + 1
SELECT #nextDate = DATEADD(day,1,#nextDate),
#WeekOfMonthNum
= DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH,0, #nextDate),0), #nextDate) + 1,
#WeekOfMonthDes
= CASE #WeekOfMonthNum
WHEN '1' THEN 'First'
WHEN '2' THEN 'Second'
WHEN '3' THEN 'Third'
WHEN '4' THEN 'Fourth'
WHEN '5' THEN 'Fifth'
WHEN '6' THEN 'Sixth'
END,
#DayName
= DATENAME(weekday, #nextDate)
CONTINUE
END
WHILE(#nextDay <=31)
BEGIN
INSERT INTO #retValue values (#nextDay,#nextDate, 0, '', '')
SELECT #nextDay=#nextDay + 1
END
RETURN
END
Make a cross join with the dates and check for the comparison of month and dates.

In less than a month:
SELECT * FROM people WHERE MOD( DATEDIFF( CURDATE( ) , `date_birth`) /30, 12 ) <1 and (((month(`date_birth`)) = (month(curdate())) and (day(`date_birth`)) > (day (curdate() ))) or ((month(`date_birth`)) > (month(curdate())) and (day(`date_birth`)) < (day (curdate() ))))

You could use DATE_FORMAT to extract the day and month parts of the birthday dates.
EDIT: sorry i didn't see that he wasn't using MySQL.

Assuming this is T-SQL, use DATEPART to compare the month and date separately.
http://msdn.microsoft.com/en-us/library/ms174420.aspx
Alternatively, subtract January 1st of the current year from everyone's birthday, and then compare using the year 1900 (or whatever your epoch year is).

Most of these solutions are close, but you have to remember a few extra scenarios. When working with birthdays and a sliding scale, you must be able to handle the transition into the next month.
For example Stephens example works great for birthdays up until the last 4 days of the month. Then you have a logic fault as the valid dates if today was the 29th would be :29, 30, AND then 1, 2, 3 of the NEXT month, so you have to condition for that as well.
An alternative would be to parse the date from the birthday field, and sub in the current year, then do a standard range comparison.

Another thought: Add their age in whole years to their birthday (or one more if their Birthday hasn't happened yet and then compare as you do above. Use DATEPART and DATEADD to do this.
http://msdn.microsoft.com/en-us/library/ms186819.aspx
The edge case of a range spanning the year would have to have special code.
Bonus tip: consider using BETWEEN...AND instead of repeating the Birthday operand.

This should work...
DECLARE #endDate DATETIME
DECLARE #today DATETIME
SELECT #endDate = getDate()+6, #today = getDate()
SELECT * FROM Employees
WHERE
(DATEPART (month, birthday) >= DATEPART (month, #today)
AND DATEPART (day, birthday) >= DATEPART (day, #today))
AND
(DATEPART (month, birthday) < DATEPART (month, #endDate)
AND DATEPART (day, birthday) < DATEPART (day, #endDate))

I faced the same problem with my college project a few years ago. I responded (in a rather weasel way) by splitting the year and the date(MM:DD) in two separate columns. And before that, my project mate was simply getting all the dates and programatically going through them. We changed that because it was too inefficient - not that my solution was any more elegant either. Also, its probably not possible to do in a database that has been in use for a while by multiple apps.

Give this a try:
SELECT * FROM Employees
WHERE DATEADD(yyyy, DATEPART(yyyy, #Today)-DATEPART(yyyy, Birthday), Birthday) > #Today
AND DATEADD(yyyy, DATEPART(yyyy, #Today)-DATEPART(yyyy, Birthday), Birthday) < DATEADD(dd, #NumDays, #Today)

Nuts! A good solution between when I started thinking about this and when I came back to answer. :)
I came up with:
select (365 + datediff(d,getdate(),cast(cast(datepart(yy,getdate()) as varchar(4)) + '-' + cast(datepart(m,birthdt) as varchar(2)) + '-' + cast(datepart(d,birthdt) as varchar(2)) as datetime))) % 365
from employees
where (365 + datediff(d,getdate(),cast(cast(datepart(yy,getdate()) as varchar(4)) + '-' + cast(datepart(m,birthdt) as varchar(2)) + '-' + cast(datepart(d,birthdt) as varchar(2)) as datetime))) % 365 < #NumDays
You don't need to cast getdate() as a datetime, right?

This is a combination of a couple of the answers that was tested. This will find the next brithday after a certain date and the age they will be. Also the numdays will limit the range you are looking 7 days = week etc.
SELECT DISTINCT FLOOR(DATEDIFF(dd,Birthday, #BeginDate) / 365.25) + 1 age,
DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, #BeginDate) / 365.25) + 1, Birthday) nextbirthday, birthday
FROM table
WHERE DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, #BeginDate) / 365.25) + 1, Birthday) > #BeginDate
AND DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, #BeginDate) / 365.25) + 1, Birthday) < DATEADD(dd, #NumDays, #BeginDate)
order by nextbirthday

The best way to achieve the same is
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SELECT Member.* from vwMember AS Member
WHERE (DATEADD(YEAR, (DATEPART(YEAR, #StartDate) -
DATEPART(YEAR, Member.dBirthDay)), Member.dBirthDay)
BETWEEN #StartDate AND #EndDate)

Upcoming Birthday for the Employee - Sqlserver
DECLARE #sam TABLE
(
EmployeeIDs int,
dob datetime
)
INSERT INTO #sam (dob, EmployeeIDs)
SELECT DOBirth, EmployeeID FROM Employee
SELECT *
FROM
(
SELECT *, bd_this_year = DATEADD(YEAR, DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, dob), dob)
FROM #sam s
) d
WHERE d.bd_this_year > DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)
AND d.bd_this_year <= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 3)

I used this for MySQL, probably not the most efficient way to query but simple enough to implement.
select * from `schema`.`table` where date_format(birthday,'%m%d') >= date_format(now(),'%m%d') and date_format(birthday,'%m%d') < date_format(DATE_ADD(NOW(), INTERVAL 5 DAY),'%m%d');

i believe this ticket has been closed ages ago but for the benefit of getting the correct sql query please have a look.
SELECT Employee_Name, DATE_OF_BIRTH
FROM Hr_table
WHERE
/**
fetching the original birth_date and replacing the birth year to the current but have to deduct 7 days to adjust jan 1-7 birthdate.
**/
datediff(d,getdate(),DATEADD(year,datediff(year,DATEADD(d,-7,hr.DATE_OF_BIRTH),getdate()),hr.date_of_birth)) between 0 and 7
-- current date looks ahead to 7 days for upcoming modified year birth date.
order by
-- sort by no of days before the birthday
datediff(d,getdate(),DATEADD(year,datediff(year,DATEADD(d,-7,hr.DATE_OF_BIRTH),getdate()),hr.date_of_birth))

Better and easy solution:
select * from users with(nolock)
where date_of_birth is not null
and
(
DATEDIFF(dd,
DATEADD(yy, -(YEAR(GETDATE())-1900),GETDATE()), --Today
DATEADD(yy, -(YEAR(date_of_birth)-1901),date_of_birth)
) % 365
) = 30

I hope this helps u in some way....
select Employeename,DOB
from Employeemaster
where day(Dob)>day(getdate()) and month(DOB)>=month(getDate())

This solution also takes care for birthdays in the next year and the ordering:
(dob = day of birth; bty = birthday this year; nbd = next birthday)
with rs (bty) as (
SELECT DATEADD(Year, DATEPART(Year, GETDATE()) - DATEPART(Year, dob), dob) as bty FROM Employees
),
rs2 (nbd) as (
select case when bty < getdate() then DATEADD(yyyy, 1, bty) else bty end as nbd from rs
)
select nbd, DATEDIFF(d, getdate(), nbd) as diff from rs2 where DATEDIFF(d, getdate(), nbd) < 14 order by diff
This version, which avoids comparison of the dates, could be faster:
with rs (dob, bty) as (
SELECT dob, DATEADD(Year, DATEPART(Year, GETDATE()) - DATEPART(Year, DOB), DOB) as bty FROM employee
),
rs2 (dob, nbd) as (
select dob, DATEADD(yyyy, FLOOR(ABS((-1*(SIGN(DATEDIFF(d, getdate(), bty))))+0.1)), bty) as nbd from rs
),
rs3 (dob, diff) as (
select dob, datediff(d, getdate(), nbd) as diff from rs2
)
select dob, diff from rs3 where diff < 14 order by diff
If the range covers the 29 of February in the next year, then use:
with rs (dob, ydiff) as (
select dob, DATEPART(Year, GETDATE()) - DATEPART(Year, DOB) as ydiff from Employee
),
rs2 (dob, bty, ydiff) as (
select dob, DATEADD(Year, ydiff, dob) as bty, ydiff from rs
),
rs3 (dob, nbd) as (
select dob, DATEADD(yyyy, FLOOR(ABS((-1*(SIGN(DATEDIFF(d, getdate(), bty))))+0.1)) + ydiff, dob) as nbd from rs2
),
rs4 (dob, ddiff, nbd) as (
select dob, datediff(d, getdate(), nbd) as diff, nbd from rs3
)
select dob, nbd, ddiff from rs4 where ddiff < 68 order by ddiff

You can also use DATEPART:
-- To find out Today's Birthday
DECLARE #today DATETIME
SELECT #today = getdate()
SELECT *
FROM SMIS_Registration
WHERE (DATEPART (month, DOB) >= DATEPART (month, #today)
AND DATEPART (day, DOB) = DATEPART (day, #today))

Below query will return all next birthday of employee, it is shortest query.
SELECT
Employee.DOB,
DATEADD(
mm,
(
(
(
(
DATEPART(yyyy, getdate())-DATEPART(yyyy, Employee.DOB )
)
+
(
1-
(
((DATEPART(mm, Employee.DOB)*100)+DATEPART(dd, Employee.DOB))
/
((DATEPART(mm, getdate())*100) + DATEPART(dd, getdate()))
)
)
)
*12
)
),
Employee.DOB
) NextDOB
FROM
Employee
ORDER BY
NextDOB ;
Above query will cover all next month excluding current date.

Solution for SQLite3:
SELECT
*,
strftime('%j', birthday) - strftime('%j', 'now') AS days_remaining
FROM
person
WHERE :n_days >= CASE
WHEN days_remaining >= 0 THEN days_remaining
ELSE days_remaining + strftime('%j', strftime('%Y-12-31', 'now'))
END
;
The solutions dividing by 325.25 to get the age, or bringing the birthdate to the current year etc. didn't work for me.
What this does is computes the delta of the two daysOfTheYear (1-366). If the birthday didn't happen yet this year, you automatically get the correct number of remaining days, which you can compare to.
If the birthday already happened, remaining_days will be negative, and you can get the correct number of remaining days by still adding the total amount of days in the current year. This also correctly handles leap years, since in that case the extra day will be added as well (By using dayOfYear(Dec 31.))

select BirthDate,Name from Employees
order by Case
WHEN convert(nvarchar(5),BirthDate,101) > convert(nvarchar(5),GETDATE(),101) then 2
WHEN convert(nvarchar(5),BirthDate,101) < convert(nvarchar(5),GETDATE(),101) then 3
WHEN convert(nvarchar(5),BirthDate,101) = convert(nvarchar(5),GETDATE(),101) then 1 else 4 end ,convert(nvarchar(2),BirthDate,101),convert(nvarchar(2),BirthDate,105)

Related

Datetime2 Overflow Issue [duplicate]

I have a table listing people along with their date of birth (currently a nvarchar(25))
How can I convert that to a date, and then calculate their age in years?
My data looks as follows
ID Name DOB
1 John 1992-01-09 00:00:00
2 Sally 1959-05-20 00:00:00
I would like to see:
ID Name AGE DOB
1 John 17 1992-01-09 00:00:00
2 Sally 50 1959-05-20 00:00:00
There are issues with leap year/days and the following method, see the update below:
try this:
DECLARE #dob datetime
SET #dob='1992-01-09 00:00:00'
SELECT DATEDIFF(hour,#dob,GETDATE())/8766.0 AS AgeYearsDecimal
,CONVERT(int,ROUND(DATEDIFF(hour,#dob,GETDATE())/8766.0,0)) AS AgeYearsIntRound
,DATEDIFF(hour,#dob,GETDATE())/8766 AS AgeYearsIntTrunc
OUTPUT:
AgeYearsDecimal AgeYearsIntRound AgeYearsIntTrunc
--------------------------------------- ---------------- ----------------
17.767054 18 17
(1 row(s) affected)
UPDATE here are some more accurate methods:
BEST METHOD FOR YEARS IN INT
DECLARE #Now datetime, #Dob datetime
SELECT #Now='1990-05-05', #Dob='1980-05-05' --results in 10
--SELECT #Now='1990-05-04', #Dob='1980-05-05' --results in 9
--SELECT #Now='1989-05-06', #Dob='1980-05-05' --results in 9
--SELECT #Now='1990-05-06', #Dob='1980-05-05' --results in 10
--SELECT #Now='1990-12-06', #Dob='1980-05-05' --results in 10
--SELECT #Now='1991-05-04', #Dob='1980-05-05' --results in 10
SELECT
(CONVERT(int,CONVERT(char(8),#Now,112))-CONVERT(char(8),#Dob,112))/10000 AS AgeIntYears
you can change the above 10000 to 10000.0 and get decimals, but it will not be as accurate as the method below.
BEST METHOD FOR YEARS IN DECIMAL
DECLARE #Now datetime, #Dob datetime
SELECT #Now='1990-05-05', #Dob='1980-05-05' --results in 10.000000000000
--SELECT #Now='1990-05-04', #Dob='1980-05-05' --results in 9.997260273973
--SELECT #Now='1989-05-06', #Dob='1980-05-05' --results in 9.002739726027
--SELECT #Now='1990-05-06', #Dob='1980-05-05' --results in 10.002739726027
--SELECT #Now='1990-12-06', #Dob='1980-05-05' --results in 10.589041095890
--SELECT #Now='1991-05-04', #Dob='1980-05-05' --results in 10.997260273973
SELECT 1.0* DateDiff(yy,#Dob,#Now)
+CASE
WHEN #Now >= DATEFROMPARTS(DATEPART(yyyy,#Now),DATEPART(m,#Dob),DATEPART(d,#Dob)) THEN --birthday has happened for the #now year, so add some portion onto the year difference
( 1.0 --force automatic conversions from int to decimal
* DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,#Now),DATEPART(m,#Dob),DATEPART(d,#Dob)),#Now) --number of days difference between the #Now year birthday and the #Now day
/ DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,#Now),1,1),DATEFROMPARTS(DATEPART(yyyy,#Now)+1,1,1)) --number of days in the #Now year
)
ELSE --birthday has not been reached for the last year, so remove some portion of the year difference
-1 --remove this fractional difference onto the age
* ( -1.0 --force automatic conversions from int to decimal
* DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,#Now),DATEPART(m,#Dob),DATEPART(d,#Dob)),#Now) --number of days difference between the #Now year birthday and the #Now day
/ DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,#Now),1,1),DATEFROMPARTS(DATEPART(yyyy,#Now)+1,1,1)) --number of days in the #Now year
)
END AS AgeYearsDecimal
Gotta throw this one out there. If you convert the date using the 112 style (yyyymmdd) to a number you can use a calculation like this...
(yyyyMMdd - yyyyMMdd) / 10000 = difference in full years
declare #as_of datetime, #bday datetime;
select #as_of = '2009/10/15', #bday = '1980/4/20'
select
Convert(Char(8),#as_of,112),
Convert(Char(8),#bday,112),
0 + Convert(Char(8),#as_of,112) - Convert(Char(8),#bday,112),
(0 + Convert(Char(8),#as_of,112) - Convert(Char(8),#bday,112)) / 10000
output
20091015 19800420 290595 29
I have used this query in our production code for nearly 10 years:
SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
You need to consider the way the datediff command rounds.
SELECT CASE WHEN dateadd(year, datediff (year, DOB, getdate()), DOB) > getdate()
THEN datediff(year, DOB, getdate()) - 1
ELSE datediff(year, DOB, getdate())
END as Age
FROM <table>
Which I adapted from here.
Note that it will consider 28th February as the birthday of a leapling for non-leap years e.g. a person born on 29 Feb 2020 will be considered 1 year old on 28 Feb 2021 instead of 01 Mar 2021.
So many of the above solutions are wrong DateDiff(yy,#Dob, #PassedDate) will not consider the month and day of both dates. Also taking the dart parts and comparing only works if they're properly ordered.
THE FOLLOWING CODE WORKS AND IS VERY SIMPLE:
create function [dbo].[AgeAtDate](
#DOB datetime,
#PassedDate datetime
)
returns int
with SCHEMABINDING
as
begin
declare #iMonthDayDob int
declare #iMonthDayPassedDate int
select #iMonthDayDob = CAST(datepart (mm,#DOB) * 100 + datepart (dd,#DOB) AS int)
select #iMonthDayPassedDate = CAST(datepart (mm,#PassedDate) * 100 + datepart (dd,#PassedDate) AS int)
return DateDiff(yy,#DOB, #PassedDate)
- CASE WHEN #iMonthDayDob <= #iMonthDayPassedDate
THEN 0
ELSE 1
END
End
EDIT: THIS ANSWER IS INCORRECT. I leave it in here as a warning to anyone tempted to use dayofyear, with a further edit at the end.
If, like me, you do not want to divide by fractional days or risk rounding/leap year errors, I applaud #Bacon Bits comment in a post above https://stackoverflow.com/a/1572257/489865 where he says:
If we're talking about human ages, you should calculate it the way
humans calculate age. It has nothing to do with how fast the earth
moves and everything to do with the calendar. Every time the same
month and day elapses as the date of birth, you increment age by 1.
This means the following is the most accurate because it mirrors what
humans mean when they say "age".
He then offers:
DATEDIFF(yy, #date, GETDATE()) -
CASE WHEN (MONTH(#date) > MONTH(GETDATE())) OR (MONTH(#date) = MONTH(GETDATE()) AND DAY(#date) > DAY(GETDATE()))
THEN 1 ELSE 0 END
There are several suggestions here involving comparing the month & day (and some get it wrong, failing to allow for the OR as correctly here!). But nobody has offered dayofyear, which seems so simple and much shorter. I offer:
DATEDIFF(year, #date, GETDATE()) -
CASE WHEN DATEPART(dayofyear, #date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END
[Note: Nowhere in SQL BOL/MSDN is what DATEPART(dayofyear, ...) returns actually documented! I understand it to be a number in the range 1--366; most importantly, it does not change by locale as per DATEPART(weekday, ...) & SET DATEFIRST.]
EDIT: Why dayofyear goes wrong: As user #AeroX has commented, if the birth/start date is after February in a non leap year, the age is incremented one day early when the current/end date is a leap year, e.g. '2015-05-26', '2016-05-25' gives an age of 1 when it should still be 0. Comparing the dayofyear in different years is clearly dangerous. So using MONTH() and DAY() is necessary after all.
I believe this is similar to other ones posted here.... but this solution worked for the leap year examples 02/29/1976 to 03/01/2011 and also worked for the case for the first year.. like 07/04/2011 to 07/03/2012 which the last one posted about leap year solution did not work for that first year use case.
SELECT FLOOR(DATEDIFF(DAY, #date1 , #date2) / 365.25)
Found here.
Since there isn't one simple answer that always gives the correct age, here's what I came up with.
SELECT DATEDIFF(YY, DateOfBirth, GETDATE()) -
CASE WHEN RIGHT(CONVERT(VARCHAR(6), GETDATE(), 12), 4) >=
RIGHT(CONVERT(VARCHAR(6), DateOfBirth, 12), 4)
THEN 0 ELSE 1 END AS AGE
This gets the year difference between the birth date and the current date. Then it subtracts a year if the birthdate hasn't passed yet.
Accurate all the time - regardless of leap years or how close to the birthdate.
Best of all - no function.
I've done a lot of thinking and searching about this and I have 3 solutions that
calculate age correctly
are short (mostly)
are (mostly) very understandable.
Here are testing values:
DECLARE #NOW DATETIME = '2013-07-04 23:59:59'
DECLARE #DOB DATETIME = '1986-07-05'
Solution 1: I found this approach in one js library. It's my favourite.
DATEDIFF(YY, #DOB, #NOW) -
CASE WHEN DATEADD(YY, DATEDIFF(YY, #DOB, #NOW), #DOB) > #NOW THEN 1 ELSE 0 END
It's actually adding difference in years to DOB and if it is bigger than current date then subtracts one year. Simple right? The only thing is that difference in years is duplicated here.
But if you don't need to use it inline you can write it like this:
DECLARE #AGE INT = DATEDIFF(YY, #DOB, #NOW)
IF DATEADD(YY, #AGE, #DOB) > #NOW
SET #AGE = #AGE - 1
Solution 2: This one I originally copied from #bacon-bits. It's the easiest to understand but a bit long.
DATEDIFF(YY, #DOB, #NOW) -
CASE WHEN MONTH(#DOB) > MONTH(#NOW)
OR MONTH(#DOB) = MONTH(#NOW) AND DAY(#DOB) > DAY(#NOW)
THEN 1 ELSE 0 END
It's basically calculating age as we humans do.
Solution 3: My friend refactored it into this:
DATEDIFF(YY, #DOB, #NOW) -
CEILING(0.5 * SIGN((MONTH(#DOB) - MONTH(#NOW)) * 50 + DAY(#DOB) - DAY(#NOW)))
This one is the shortest but it's most difficult to understand. 50 is just a weight so the day difference is only important when months are the same. SIGN function is for transforming whatever value it gets to -1, 0 or 1. CEILING(0.5 * is the same as Math.max(0, value) but there is no such thing in SQL.
What about:
DECLARE #DOB datetime
SET #DOB='19851125'
SELECT Datepart(yy,convert(date,GETDATE())-#DOB)-1900
Wouldn't that avoid all those rounding, truncating and ofsetting issues?
Just check whether the below answer is feasible.
DECLARE #BirthDate DATE = '09/06/1979'
SELECT
(
YEAR(GETDATE()) - YEAR(#BirthDate) -
CASE WHEN (MONTH(GETDATE()) * 100) + DATEPART(dd, GETDATE()) >
(MONTH(#BirthDate) * 100) + DATEPART(dd, #BirthDate)
THEN 1
ELSE 0
END
)
select floor((datediff(day,0,#today) - datediff(day,0,#birthdate)) / 365.2425) as age
There are a lot of 365.25 answers here. Remember how leap years are defined:
Every four years
except every 100 years
except every 400 years
There are many answers to this question, but I think this one is close to the truth.
The datediff(year,…,…) function, as we all know, only counts the boundaries crossed by the date part, in this case the year. As a result it ignores the rest of the year.
This will only give the age in completed years if the year were to start on the birthday. It probably doesn’t, but we can fake it by adjusting the asking date back by the same amount.
In pseudopseudo code, it’s something like this:
adjusted_today = today - month(dob) + 1 - day(dob) + 1
age = year(adjusted_today - dob)
The + 1 is to allow for the fact that the month and day numbers start from 1 and not 0.
The reason we subtract the month and the day separately rather than the day of the year is because February has the annoying tendency to change its length.
The calculation in SQL is:
datediff(year,dob,dateadd(month,-month(dob)+1,dateadd(day,-day(dob)+1,today)))
where dob and today are presumed to be the date of birth and the asking date.
You can test this as follows:
WITH dates AS (
SELECT
cast('2022-03-01' as date) AS today,
cast('1943-02-25' as date) AS dob
)
select
datediff(year,dob,dateadd(month,-month(dob)+1,dateadd(day,-day(dob)+1,today))) AS age
from dates;
which gives you George Harrison’s age in completed years.
This is much cleaner than fiddling about with quarter days which will generally give you misleading values on the edges.
If you have the luxury of creating a scalar function, you can use something like this:
DROP FUNCTION IF EXISTS age;
GO
CREATE FUNCTION age(#dob date, #today date) RETURNS INT AS
BEGIN
SET #today = dateadd(month,-month(#dob)+1,#today);
SET #today = dateadd(day,-day(#dob)+1,#today);
RETURN datediff(year,#dob,#today);
END;
GO
Remember, you need to call dbo.age() because, well, Microsoft.
DECLARE #DOB datetime
set #DOB ='11/25/1985'
select floor(
( cast(convert(varchar(8),getdate(),112) as int)-
cast(convert(varchar(8),#DOB,112) as int) ) / 10000
)
source: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/
Try This
DECLARE #date datetime, #tmpdate datetime, #years int, #months int, #days int
SELECT #date = '08/16/84'
SELECT #tmpdate = #date
SELECT #years = DATEDIFF(yy, #tmpdate, GETDATE()) - CASE WHEN (MONTH(#date) > MONTH(GETDATE())) OR (MONTH(#date) = MONTH(GETDATE()) AND DAY(#date) > DAY(GETDATE())) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(yy, #years, #tmpdate)
SELECT #months = DATEDIFF(m, #tmpdate, GETDATE()) - CASE WHEN DAY(#date) > DAY(GETDATE()) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(m, #months, #tmpdate)
SELECT #days = DATEDIFF(d, #tmpdate, GETDATE())
SELECT Convert(Varchar(Max),#years)+' Years '+ Convert(Varchar(max),#months) + ' Months '+Convert(Varchar(Max), #days)+'days'
After trying MANY methods, this works 100% of the time using the modern MS SQL FORMAT function instead of convert to style 112. Either would work but this is the least code.
Can anyone find a date combination which does not work? I don't think there is one :)
--Set parameters, or choose from table.column instead:
DECLARE #DOB DATE = '2000/02/29' -- If #DOB is a leap day...
,#ToDate DATE = '2018/03/01' --...there birthday in this calculation will be
--0+ part tells SQL to calc the char(8) as numbers:
SELECT [Age] = (0+ FORMAT(#ToDate,'yyyyMMdd') - FORMAT(#DOB,'yyyyMMdd') ) /10000
CASE WHEN datepart(MM, getdate()) < datepart(MM, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTH_DATE)) -1 )
ELSE
CASE WHEN datepart(MM, getdate()) = datepart(MM, BIRTHDATE)
THEN
CASE WHEN datepart(DD, getdate()) < datepart(DD, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) -1 )
ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE))
END
ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) END
END
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable
How about this:
SET #Age = CAST(DATEDIFF(Year, #DOB, #Stamp) as int)
IF (CAST(DATEDIFF(DAY, DATEADD(Year, #Age, #DOB), #Stamp) as int) < 0)
SET #Age = #Age - 1
Try this solution:
declare #BirthDate datetime
declare #ToDate datetime
set #BirthDate = '1/3/1990'
set #ToDate = '1/2/2008'
select #BirthDate [Date of Birth], #ToDate [ToDate],(case when (DatePart(mm,#ToDate) < Datepart(mm,#BirthDate))
OR (DatePart(m,#ToDate) = Datepart(m,#BirthDate) AND DatePart(dd,#ToDate) < Datepart(dd,#BirthDate))
then (Datepart(yy, #ToDate) - Datepart(yy, #BirthDate) - 1)
else (Datepart(yy, #ToDate) - Datepart(yy, #BirthDate))end) Age
This will correctly handle the issues with the birthday and rounding:
DECLARE #dob datetime
SET #dob='1992-01-09 00:00:00'
SELECT DATEDIFF(YEAR, '0:0', getdate()-#dob)
Ed Harper's solution is the simplest I have found which never returns the wrong answer when the month and day of the two dates are 1 or less days apart. I made a slight modification to handle negative ages.
DECLARE #D1 AS DATETIME, #D2 AS DATETIME
SET #D2 = '2012-03-01 10:00:02'
SET #D1 = '2013-03-01 10:00:01'
SELECT
DATEDIFF(YEAR, #D1,#D2)
+
CASE
WHEN #D1<#D2 AND DATEADD(YEAR, DATEDIFF(YEAR,#D1, #D2), #D1) > #D2
THEN - 1
WHEN #D1>#D2 AND DATEADD(YEAR, DATEDIFF(YEAR,#D1, #D2), #D1) < #D2
THEN 1
ELSE 0
END AS AGE
The answer marked as correct is nearer to accuracy but, it fails in following scenario - where Year of birth is Leap year and day are after February month
declare #ReportStartDate datetime = CONVERT(datetime, '1/1/2014'),
#DateofBirth datetime = CONVERT(datetime, '2/29/1948')
FLOOR(DATEDIFF(HOUR,#DateofBirth,#ReportStartDate )/8766)
OR
FLOOR(DATEDIFF(HOUR,#DateofBirth,#ReportStartDate )/8765.82) -- Divisor is more accurate than 8766
-- Following solution is giving me more accurate results.
FLOOR(DATEDIFF(YEAR,#DateofBirth,#ReportStartDate) - (CASE WHEN DATEADD(YY,DATEDIFF(YEAR,#DateofBirth,#ReportStartDate),#DateofBirth) > #ReportStartDate THEN 1 ELSE 0 END ))
It worked in almost all scenarios, considering leap year, date as 29 feb, etc.
Please correct me if this formula have any loophole.
Declare #dob datetime
Declare #today datetime
Set #dob = '05/20/2000'
set #today = getdate()
select CASE
WHEN dateadd(year, datediff (year, #dob, #today), #dob) > #today
THEN datediff (year, #dob, #today) - 1
ELSE datediff (year, #dob, #today)
END as Age
Here is how i calculate age given a birth date and current date.
select case
when cast(getdate() as date) = cast(dateadd(year, (datediff(year, '1996-09-09', getdate())), '1996-09-09') as date)
then dateDiff(yyyy,'1996-09-09',dateadd(year, 0, getdate()))
else dateDiff(yyyy,'1996-09-09',dateadd(year, -1, getdate()))
end as MemberAge
go
CREATE function dbo.AgeAtDate(
#DOB datetime,
#CompareDate datetime
)
returns INT
as
begin
return CASE WHEN #DOB is null
THEN
null
ELSE
DateDiff(yy,#DOB, #CompareDate)
- CASE WHEN datepart(mm,#CompareDate) > datepart(mm,#DOB) OR (datepart(mm,#CompareDate) = datepart(mm,#DOB) AND datepart(dd,#CompareDate) >= datepart(dd,#DOB))
THEN 0
ELSE 1
END
END
End
GO
DECLARE #FromDate DATETIME = '1992-01-2623:59:59.000',
#ToDate DATETIME = '2016-08-10 00:00:00.000',
#Years INT, #Months INT, #Days INT, #tmpFromDate DATETIME
SET #Years = DATEDIFF(YEAR, #FromDate, #ToDate)
- (CASE WHEN DATEADD(YEAR, DATEDIFF(YEAR, #FromDate, #ToDate),
#FromDate) > #ToDate THEN 1 ELSE 0 END)
SET #tmpFromDate = DATEADD(YEAR, #Years , #FromDate)
SET #Months = DATEDIFF(MONTH, #tmpFromDate, #ToDate)
- (CASE WHEN DATEADD(MONTH,DATEDIFF(MONTH, #tmpFromDate, #ToDate),
#tmpFromDate) > #ToDate THEN 1 ELSE 0 END)
SET #tmpFromDate = DATEADD(MONTH, #Months , #tmpFromDate)
SET #Days = DATEDIFF(DAY, #tmpFromDate, #ToDate)
- (CASE WHEN DATEADD(DAY, DATEDIFF(DAY, #tmpFromDate, #ToDate),
#tmpFromDate) > #ToDate THEN 1 ELSE 0 END)
SELECT #FromDate FromDate, #ToDate ToDate,
#Years Years, #Months Months, #Days Days
What about a solution with only date functions, not math, not worries about leap year
CREATE FUNCTION dbo.getAge(#dt datetime)
RETURNS int
AS
BEGIN
RETURN
DATEDIFF(yy, #dt, getdate())
- CASE
WHEN
MONTH(#dt) > MONTH(GETDATE()) OR
(MONTH(#dt) = MONTH(GETDATE()) AND DAY(#dt) > DAY(GETDATE()))
THEN 1
ELSE 0
END
END
declare #birthday as datetime
set #birthday = '2000-01-01'
declare #today as datetime
set #today = GetDate()
select
case when ( substring(convert(varchar, #today, 112), 5,4) >= substring(convert(varchar, #birthday, 112), 5,4) ) then
(datepart(year,#today) - datepart(year,#birthday))
else
(datepart(year,#today) - datepart(year,#birthday)) - 1
end
The following script checks the difference in years between now and the given date of birth; the second part checks whether the birthday is already past in the current year; if not, it subtracts it:
SELECT year(NOW()) - year(date_of_birth) - (CONCAT(year(NOW()), '-', month(date_of_birth), '-', day(date_of_birth)) > NOW()) AS Age
FROM tableName;

SQL to calculate age based on year and month

I want to get a list of names who are turning 14 year old and 11 months. Can someone to help me fix the SQL below? Thanks!
SELECT NAME
FROM TABLE
WHERE
(cast(datediff(DAY, DOB, getDate() -1) /(365.23076923074) as int)>=14
AND cast(datediff(MONTH, DOB, getDate() -1) % (12) as int)>=11)
OR
(cast(datediff(DAY, DOB, getDate() -1) /(365.23076923074) as int)<=15
AND cast(datediff(MONTH, DOB, getDate() -1) % (12) as int)<=0)
These people turn 14, 11 months, today.
select name
from table
where DOB = cast(dateadd(mm,-11,dateadd(yy,-14,getdate())) as date)
--this is to eliminate the time on getdate
I am not sure if you need <= or just <. Please make changes accordingly.
select NAME from table Where DOB < = DATEADD(MONTH, -179, GETDATE())
Or If you are just looking for results between a span of one month then:
select NAME from table Where DOB BETWEEN DATEADD(MONTH, -179, GETDATE()) AND DATEADD(MONTH, -180, GETDATE())

Group days by week

Is there is a way to group dates by week of month in SQL Server?
For example
Week 2: 05/07/2012 - 05/13/2012
Week 3: 05/14/2012 - 05/20/2012
but with Sql server statement
I tried
SELECT SOMETHING,
datediff(wk, convert(varchar(6), getdate(), 112) + '01', getdate()) + 1 AS TIME_
FROM STATISTICS_
GROUP BY something, TIME_
ORDER BY TIME_
but it returns the week number of month. (means 3)
How to get the pair of days for current week ?
For example, now we are in third (3rd) week and I want to show 05/14/2012 - 05/20/2012
I solved somehow:
SELECT DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)
select DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)+6
Then I will get two days and I will concatenate them later.
All right, bear with me here. We're going to build a temporary calendar table that represents this month, including the days from before and after the month that fall into your definition of a week (Monday - Sunday). I do this in a lot of steps to try to make the process clear, but I probably haven't excelled at that in this case.
We can then generate the ranges for the different weeks, and you can join against your other tables using that.
SET DATEFIRST 7;
SET NOCOUNT ON;
DECLARE #today SMALLDATETIME, #fd SMALLDATETIME, #rc INT;
SELECT #today = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0), -- today
#fd = DATEADD(DAY, 1-DAY(#today), #today), -- first day of this month
#rc = DATEPART(DAY, DATEADD(DAY, -1, DATEADD(MONTH, 1, #fd)));-- days in month
DECLARE #thismonth TABLE (
[date] SMALLDATETIME,
[weekday] TINYINT,
[weeknumber] TINYINT
);
;WITH n(d) AS (
SELECT TOP (#rc+12) DATEADD(DAY, ROW_NUMBER() OVER
(ORDER BY [object_id]) - 7, #fd) FROM sys.all_objects
)
INSERT #thismonth([date], [weekday]) SELECT d, DATEPART(WEEKDAY, d) FROM n;
DELETE #thismonth WHERE [date] < (SELECT MIN([date]) FROM #thismonth WHERE [weekday] = 2)
OR [date] > (SELECT MAX([date]) FROM #thismonth WHERE [weekday] = 1);
;WITH x AS ( SELECT [date], weeknumber, rn = ((ROW_NUMBER() OVER
(ORDER BY [date])-1) / 7) + 1 FROM #thismonth ) UPDATE x SET weeknumber = rn;
-- now, the final query given all that (I've only broken this up to get rid of the vertical scrollbars):
;WITH ranges(w,s,e) AS (
SELECT weeknumber, MIN([date]), MAX([date]) FROM #thismonth GROUP BY weeknumber
)
SELECT [week] = CONVERT(CHAR(10), r.s, 120) + ' - ' + CONVERT(CHAR(10), r.e, 120)
--, SOMETHING , other columns from STATISTICS_?
FROM ranges AS r
-- LEFT OUTER JOIN dbo.STATISTICS_ AS s
-- ON s.TIME_ >= r.s AND s.TIME_ < DATEADD(DAY, 1, r.e)
-- comment this out if you want all the weeks from this month:
WHERE w = (SELECT weeknumber FROM #thismonth WHERE [date] = #today)
GROUP BY r.s, r.e --, SOMETHING
ORDER BY [week];
Results with WHERE clause:
week
-----------------------
2012-05-14 - 2012-05-20
Results without WHERE clause:
week
-----------------------
2012-04-30 - 2012-05-06
2012-05-07 - 2012-05-13
2012-05-14 - 2012-05-20
2012-05-21 - 2012-05-27
2012-05-28 - 2012-06-03
Note that I chose YYYY-MM-DD on purpose. You should avoid regional formatting like M/D/Y especially for input but also for display. No matter how targeted you think your audience is, you're always going to have someone who thinks 05/07/2012 is July 5th, not May 7th. With YYYY-MM-DD there is no ambiguity whatsoever.
Create a calendar table, then you can query week numbers, first/last days of specific weeks and months etc. You can also join on it queries to get a date range etc.
How about a case statement?
case when datepart(day, mydatetime) between 1 and 7 then 1
when datepart(day, mydatetime) between 8 and 14 then 2
...
You'll also have to include the year & month unless you want all the week 1s in the same group.
It's not clear of you want to "group dates by week of month", or alternately "select data from a given week"
If you mean "group" this little snippet should get you 'week of month':
SELECT <stuff>
FROM CP_STATISTICS
WHERE Month(<YOUR DATE COL>) = 5 --april
GROUP BY Year(<YOUR DATE COL>),
Month(<YOUR DATE COL>),
DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, <YOUR DATE COL>), 0)
, <YOUR DATE COL>) +1
Alternately, if you want "sales for week 1 of April, ordered by date" You could do something like..
DECLARE #targetDate datetime2 = '5/3/2012'
DECLARE #targetWeek int = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, #targetDate), 0), #targetDate) +1
SELECT <stuff>
FROM CP_STATISTICS
WHERE MONTH(#targetDate) = Month(myDateCol) AND
YEAR(#targetDate) = Year (myDateCol) AND
#targetWeek = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, myDateCol), 0), myDateCol) +1
ORDER BY myDateCol
Note, things would get more complicated if you use non-standard weeks, or want to reach a few days into an earlier month for weeks that straddle a month boundary.
EDIT 2
From looking at your 'solved now' section. I think your question is "how do I get data out of a table for a given week?"
Your solution appears to be:
DECLARE #targetDate datetime2 = '5/1/2012'
DECLARE #startDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,targetDate), 0)
DECLARE #endDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,#now), 0)+6
SELECT <stuff>
FROM STATISTICS_
WHERE dateStamp >= #startDate AND dateStamp <= #endDate
Notice how if the date is 5/1 this solution results in a start date of '4/30/2012'. I point this out because your solution crosses month boundaries. This may or may not be desirable.

Getting Number of weeks in a Month from a Datetime Column

I have a table called FcData and the data looks like:
Op_Date
2011-02-14 11:53:40.000
2011-02-17 16:02:19.000
2010-02-14 12:53:40.000
2010-02-17 14:02:19.000
I am looking to get the Number of weeks in That Month from Op_Date. So I am looking for output like:
Op_Date Number of Weeks
2011-02-14 11:53:40.000 5
2011-02-17 16:02:19.000 5
2010-02-14 12:53:40.000 5
2010-02-17 14:02:19.000 5
This page has some good functions to figure out the last day of any given month: http://www.sql-server-helper.com/functions/get-last-day-of-month.aspx
Just wrap the output of that function with a DATEPART(wk, last_day_of_month) call. Combining it with an equivalent call for the 1st-day-of-week will let you get the number of weeks in that month.
Use this to get the number of week for ONE specific date. Replace GetDate() by your date:
declare #dt date = cast(GetDate() as date);
declare #dtstart date = DATEADD(day, -DATEPART(day, #dt) + 1, #dt);
declare #dtend date = dateadd(DAY, -1, DATEADD(MONTH, 1, #dtstart));
WITH dates AS (
SELECT #dtstart ADate
UNION ALL
SELECT DATEADD(day, 1, t.ADate)
FROM dates t
WHERE DATEADD(day, 1, t.ADate) <= #dtend
)
SELECT top 1 DatePart(WEEKDAY, ADate) weekday, COUNT(*) weeks
FROM dates d
group by DatePart(WEEKDAY, ADate)
order by 2 desc
Explained: the CTE creates a result set with all dates for the month of the given date. Then we query the result set, grouping by week day and count the number of occurrences. The max number will give us how many weeks the month overlaps (premise: if the month has 5 Mondays, it will cover five weeks of the year).
Update
Now, if you have multiple dates, you should tweak accordingly, joining your query with the dates CTE.
Here is my take on it, might have missed something.
In Linq:
from u in TblUsers
let date = u.CreateDate.Value
let firstDay = new DateTime(date.Year, date.Month, 1)
let lastDay = firstDay.AddMonths(1)
where u.CreateDate.HasValue
select Math.Ceiling((lastDay - firstDay).TotalDays / 7)
And generated SQL:
-- Region Parameters
DECLARE #p0 Int = 1
DECLARE #p1 Int = 1
DECLARE #p2 Float = 7
-- EndRegion
SELECT CEILING(((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t3].[value], [t3].[value2]))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t3].[value], [t3].[value2]), [t3].[value]), [t3].[value2])) * 10000))) / 864000000000) / #p2) AS [value]
FROM (
SELECT [t2].[createDate], [t2].[value], DATEADD(MONTH, #p1, [t2].[value]) AS [value2]
FROM (
SELECT [t1].[createDate], CONVERT(DATETIME, CONVERT(NCHAR(2), DATEPART(Month, [t1].[value])) + ('/' + (CONVERT(NCHAR(2), #p0) + ('/' + CONVERT(NCHAR(4), DATEPART(Year, [t1].[value]))))), 101) AS [value]
FROM (
SELECT [t0].[createDate], [t0].[createDate] AS [value]
FROM [tblUser] AS [t0]
) AS [t1]
) AS [t2]
) AS [t3]
WHERE [t3].[createDate] IS NOT NULL
According to this MSDN article: http://msdn.microsoft.com/en-us/library/ms174420.aspx you can only get the current week in the year, not what that month returns.
There may be various approaches to implementing the idea suggested by #Marc B. Here's one, where no UDFs are used but the first and the last days of month are calculated directly:
WITH SampleData AS (
SELECT CAST('20110214' AS datetime) AS Op_Date
UNION ALL SELECT '20110217'
UNION ALL SELECT '20100214'
UNION ALL SELECT '20100217'
UNION ALL SELECT '20090214'
UNION ALL SELECT '20090217'
),
MonthStarts AS (
SELECT
Op_Date,
MonthStart = DATEADD(DAY, 1 - DAY(Op_Date), Op_Date)
/* alternatively: DATEADD(MONTH, DATEDIFF(MONTH, 0, Op_Date), 0) */
FROM FcData
),
Months AS (
SELECT
Op_Date,
MonthStart,
MonthEnd = DATEADD(DAY, -1, DATEADD(MONTH, 1, MonthStart))
FROM FcData
)
Weeks AS (
SELECT
Op_Date,
StartWeek = DATEPART(WEEK, MonthStart),
EndWeek = DATEPART(WEEK, MonthEnd)
FROM MonthStarts
)
SELECT
Op_Date,
NumberOfWeeks = EndWeek - StartWeek + 1
FROM Weeks
All calculations could be done in one SELECT, but I chose to split them into steps and place every step in a separate CTE so it could be seen better how the end result was obtained.
You can get number of weeks per month using the following method.
Datepart(WEEK,
DATEADD(DAY,
-1,
DATEADD(MONTH,
1,
DATEADD(DAY,
1 - DAY(GETDATE()),
GETDATE())))
-
DATEADD(DAY,
1 - DAY(GETDATE()),
GETDATE())
+1
)
Here how you can get accurate amount of weeks:
DECLARE #date DATETIME
SET #date = GETDATE()
SELECT ROUND(cast(datediff(day, dateadd(day, 1-day(#date), #date), dateadd(month, 1, dateadd(day, 1-day(#date), #date))) AS FLOAT) / 7, 2)
With this code for Sep 2014 you'll get 4.29 which is actually true since there're 4 full weeks and 2 more days.

Get the records of last month in SQL server

I want to get the records of last month based on my db table [member] field "date_created".
What's the sql to do this?
For clarification,
last month - 1/8/2009 to 31/8/2009
If today is 3/1/2010, I'll need to get the records of 1/12/2009 to 31/12/2009.
All the existing (working) answers have one of two problems:
They will ignore indices on the column being searched
The will (potentially) select data that is not intended, silently corrupting your results.
1. Ignored Indices:
For the most part, when a column being searched has a function called on it (including implicitly, like for CAST), the optimizer must ignore indices on the column and search through every record. Here's a quick example:
We're dealing with timestamps, and most RDBMSs tend to store this information as an increasing value of some sort, usually a long or BIGINTEGER count of milli-/nanoseconds. The current time thus looks/is stored like this:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
You don't see the 'Year' value ('2014') in there, do you? In fact, there's a fair bit of complicated math to translate back and forth. So if you call any of the extraction/date part functions on the searched column, the server has to perform all that math just to figure out if you can include it in the results. On small tables this isn't an issue, but as the percentage of rows selected decreases this becomes a larger and larger drain. Then in this case, you're doing it a second time for asking about MONTH... well, you get the picture.
2. Unintended data:
Depending on the particular version of SQL Server, and column datatypes, using BETWEEN (or similar inclusive upper-bound ranges: <=) can result in the wrong data being selected. Essentially, you potentially end up including data from midnight of the "next" day, or excluding some portion of the "current" day's records.
What you should be doing:
So we need a way that's safe for our data, and will use indices (if viable). The correct way is then of the form:
WHERE date_created >= #startOfPreviousMonth AND date_created < #startOfCurrentMonth
Given that there's only one month, #startOfPreviousMonth can be easily substituted for/derived by:
DATEADD(month, -1, #startOfCurrentMonth)
If you need to derive the start-of-current-month in the server, you can do it via the following:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
A quick word of explanation here. The initial DATEDIFF(...) will get the difference between the start of the current era (0001-01-01 - AD, CE, whatever), essentially returning a large integer. This is the count of months to the start of the current month. We then add this number to the start of the era, which is at the start of the given month.
So your full script could/should look similar to the following:
DECLARE #startOfCurrentMonth DATETIME
SET #startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, #startOfCurrentMonth)
AND date_created < #startOfCurrentMonth
All date operations are thus only performed once, on one value; the optimizer is free to use indices, and no incorrect data will be included.
SELECT *
FROM Member
WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate()))
AND DATEPART(yyyy, date_created) = DATEPART(yyyy, DATEADD(m, -1, getdate()))
You need to check the month and year.
Add the options which have been provided so far won't use your indexes at all.
Something like this will do the trick, and make use of an index on the table (if one exists).
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = dateadd(mm, -1, getdate())
SET #StartDate = dateadd(dd, datepart(dd, getdate())*-1, #StartDate)
SET #EndDate = dateadd(mm, 1, #StartDate)
SELECT *
FROM Member
WHERE date_created BETWEEN #StartDate AND #EndDate
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = DATEADD(mm, DATEDIFF(mm,0,getdate())-1, 0)
SET #EndDate = DATEADD(mm, 1, #StartDate)
SELECT *
FROM Member
WHERE date_created BETWEEN #StartDate AND #EndDate
An upgrade to mrdenny's solution, this way you get exactly last month from YYYY-MM-01
Last month consider as till last day of the month.
31/01/2016 here last day of the month would be 31 Jan. which is not similar to last 30 days.
SELECT CONVERT(DATE, DATEADD(DAY,-DAY(GETDATE()),GETDATE()))
One way to do it is using the DATEPART function:
select field1, field2, fieldN from TABLE where DATEPART(month, date_created) = 4
and DATEPART(year, date_created) = 2009
will return all dates in april. For last month (ie, previous to current month) you can use GETDATE and DATEADD as well:
select field1, field2, fieldN from TABLE where DATEPART(month, date_created)
= (DATEPART(month, GETDATE()) - 1) and
DATEPART(year, date_created) = DATEPART(year, DATEADD(m, -1, GETDATE()))
declare #PrevMonth as nvarchar(256)
SELECT #PrevMonth = DateName( month,DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)) +
'-' + substring(DateName( Year, getDate() ) ,3,4)
SQL query to get record of the present month only
SELECT * FROM CUSTOMER
WHERE MONTH(DATE) = MONTH(CURRENT_TIMESTAMP) AND YEAR(DATE) = YEAR(CURRENT_TIMESTAMP);
SELECT * FROM Member WHERE month(date_created) = month(NOW() - INTERVAL 1 MONTH);
select * from [member] where DatePart("m", date_created) = DatePart("m", DateAdd("m", -1, getdate())) AND DatePart("yyyy", date_created) = DatePart("yyyy", DateAdd("m", -1, getdate()))
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)
SET #EndDate = dateadd(dd, -1, DATEADD(mm, 1, #StartDate))
SELECT * FROM Member WHERE date_created BETWEEN #StartDate AND #EndDate
and another upgrade to mrdenny's solution.
It gives the exact last day of the previous month as well.
WHERE
date_created >= DATEADD(MONTH, DATEDIFF(MONTH, 31, CURRENT_TIMESTAMP), 0)
AND date_created < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
I'm from Oracle env and I would do it like this in Oracle:
select * from table
where trunc(somedatefield, 'MONTH') =
trunc(sysdate -INTERVAL '0-1' YEAR TO MONTH, 'MONTH')
Idea: I'm running a scheduled report of previous month (from day 1 to the last day of the month, not windowed). This could be index unfriendly, but Oracle has fast date handling anyways.
Is there a similar simple and short way in MS SQL? The answer comparing year and month separately seems silly to Oracle folks.
You can get the last month records with this query
SELECT * FROM dbo.member d
WHERE CONVERT(DATE, date_created,101)>=CONVERT(DATE,DATEADD(m, datediff(m, 0, current_timestamp)-1, 0))
and CONVERT(DATE, date_created,101) < CONVERT(DATE, DATEADD(m, datediff(m, 0, current_timestamp)-1, 0),101)
I don't think the accepted solution is very index friendly
I use the following lines instead
select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date <= dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));
Or simply (this is the best).
select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date < SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
Some help
-- Get the first of last month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
-- Get the first of current month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
--Get the last of last month except the last 3milli seconds. (3miliseconds being used as SQL express otherwise round it up to the full second (SERIUSLY MS)
SELECT dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));
Here is what I did so I could put it in a view:
ALTER view [dbo].[MyView] as
with justdate as (
select getdate() as rightnow
)
, inputs as (
select dateadd(day, 1, EOMONTH(jd.rightnow, -2)) as FirstOfLastMonth
,dateadd(day, 1, EOMONTH(jd.rightnow, -1)) as FirstOfThisMonth
from justdate jd
)
SELECT TOP 10000
[SomeColumn]
,[CreatedTime]
from inputs i
join [dbo].[SomeTable]
on createdtime >= i.FirstOfLastMonth
and createdtime < i.FirstOfThisMonth
order by createdtime
;
Note that I intentionally ran getdate() once.
In Sql server for last one month:
select * from tablename
where order_date > DateAdd(WEEK, -1, GETDATE()+1) and order_date<=GETDATE()
DECLARE #curDate INT = datepart( Month,GETDATE())
IF (#curDate = 1)
BEGIN
select * from Featured_Deal
where datepart( Month,Created_Date)=12 AND datepart(Year,Created_Date) = (datepart(Year,GETDATE())-1)
END
ELSE
BEGIN
select * from Featured_Deal
where datepart( Month,Created_Date)=(datepart( Month,GETDATE())-1) AND datepart(Year,Created_Date) = datepart(Year,GETDATE())
END
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = dateadd(mm, -1, getdate())
SET #StartDate = dateadd(dd, datepart(dd, getdate())*-1, #StartDate)
SET #EndDate = dateadd(mm, 1, #StartDate)
set #StartDate = DATEADD(dd, 1 , #StartDate)
The way I fixed similar issue was by adding Month to my SELECT portion
Month DATEADD(day,Created_Date,'1971/12/31') As Month
and than I added WHERE statement
Month DATEADD(day,Created_Date,'1971/12/31') = month(getdate())-1
If you are looking for last month so try this,
SELECT
FROM #emp
WHERE DATEDIFF(MONTH,CREATEDDATE,GETDATE()) = 1
If you are looking for last month so try this,
SELECT
FROM #emp
WHERE DATEDIFF(day,CREATEDDATE,GETDATE()) between 1 and 30
A simple query which works for me is:
select * from table where DATEADD(month, 1,DATEFIELD) >= getdate()
If you are looking for previous month data:
date(date_created)>=date_sub(date_format(curdate(),"%Y-%m-01"),interval 1 month) and
date(date_created)<=date_sub(date_format(curdate(),'%Y-%m-01'),interval 1 day)
This will also work when the year changes. It will also work on MySQL.