I have a table with employee birthdays. I'm trying to create a stored procedure that returns everyone's birthdays within 2 given dates. We have employees born on a leap year.
I can successfully get return a person when their birthday falls on a leap year following the example at http://www.berezniker.com/content/pages/sql/microsoft-sql-server/birthday-query-ms-sql-server
DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = '2009-02-22'
SET @EndDate = '2009-02-28'
--SET @StartDate = '2008-02-22'
--SET @EndDate = '2008-02-29'
SELECT
FullName,
DATEPART(MONTH, dob) AS MONTH,
DATEPART(DAY, dob) AS DAY,
CONVERT(VARCHAR(10), dob, 111) AS dob
FROM
People
WHERE
DATEADD(YEAR, DATEDIFF(YEAR, dob, @StartDate), dob) BETWEEN @StartDate AND @EndDate
OR
DATEADD(YEAR, DATEDIFF(YEAR, dob, @EndDate), dob) BETWEEN @StartDate AND @EndDate
ORDER BY
CASE WHEN DATEADD(YEAR, DATEDIFF(YEAR, dob, @StartDate), dob)
BETWEEN @StartDate AND @EndDate THEN 1 ELSE 2 END,
DATEPART(MONTH, dob), DATEPART(DAY, dob)
CREATE TABLE People
(
PK INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
FullName VARCHAR(30) NOT NULL,
dob DATETIME NULL
)
GO
INSERT INTO People (FullName, dob) VALUES ('John Smith', '1965-02-28')
INSERT INTO People (FullName, dob) VALUES ('Alex Black', '1960-02-29')
INSERT INTO People (FullName, dob) VALUES ('Bill Doors', '1968-02-27')
...
--shortened for clarity
However, with the data above, my goal is to display Alex Black
's birthday for year 2014 as 2/28/2014
and for year 2016 as 2/29/2016
.
Also, if you are in the mood, my full intentions are the following:
I want to pass 2 dates, not matter how far apart: @DateFrom date = '1/1/2014'
and @DateTo date = '12/31/2016'
. The result I want back is
FULLNAME DOB
Bill Doors 2014-02-27
John Smith 2014-02-28
Alex Black 2014-02-28
Bill Doors 2015-02-27
John Smith 2015-02-28
Alex Black 2015-02-28
Bill Doors 2016-02-27
John Smith 2016-02-28
Alex Black 2016-02-29 -- note this year the date is feb 29th
you can try this
declare
@DateFrom date = '20140101',
@DateTo date = '20161231'
;with
-- All years between @DateFrom and @DateTo
CTE_Years as (
select datepart(yy, @DateFrom) as y
union all
select y + 1 as y
from CTE_Years
where y < datepart(yy, @DateTo)
),
-- Calculate leap years
CTE_Years2 as (
select
cast(y as nvarchar(4)) as y,
case
when y / 400 * 400 = y then 1
when y / 100 * 100 = y then 0
when y / 4 * 4 = y then 1
else 0
end as Is_Leap_Year
from CTE_Years
),
-- get peoples birth day and month in form 'mmdd'
CTE_People as (
select
FullName,
right(convert(nvarchar(8), dob, 112), 4) as dob
from People
),
-- get peoples birth date in given years
CTE_DOB as (
select
P.FullName,
convert(
date,
Y.y +
case
when Y.Is_Leap_Year = 0 and P.dob = '0229' then '0228'
else P.dob
end,
112
) as dob
from CTE_Years2 as Y
cross join CTE_People as P
)
-- Final query
select *
from CTE_DOB
where dob > @DateFrom and dob < @DateTo
order by DOB asc
TAKE A LOOK AT SQL FIDDLE EXAMPLE
EDIT: Lamak remind me a great way to calculate birthday, so here's edited version
declare
@DateFrom date = '1/1/2014',
@DateTo date = '12/31/2016'
;with
CTE_Years as (
select dateadd(yy, datediff(yy, 0, @DateFrom), 0) as y
union all
select dateadd(yy, 1, y) as y
from CTE_Years
where y < @DateTo
),
CTE_DOB as (
select
P.FullName,
dateadd(yy, datediff(yy, P.dob, Y.y), P.dob) as dob
from CTE_Years as Y
cross join People as P
)
select *
from CTE_DOB
where dob > @DateFrom and dob < @DateTo
order by DOB asc