我有一张员工生日的桌子。我正在尝试创建一个存储过程,在两个给定日期内返回每个人的生日。我们的员工在闰年出生。
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
但是,根据上述数据,我的目标是将Alex Black
2014年的生日显示为2/28/2014
,将2016年的生日显示为2/29/2016
。
另外,如果你心情愉快,我的全部意图如下:
我想传递两个日期,无论距离多远:@DateFrom date = '1/1/2014'
和@DateTo date = '12/31/2016'
。我想要的结果是
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
答案 0 :(得分:5)
这是一种方式:
DECLARE @StartDate DATETIME, @EndDate DATETIME, @I INT
SET @StartDate = '20140101'
SET @EndDate = '20161231'
SET @I = 0
DECLARE @Years TABLE(Years DATE)
WHILE @I <= DATEDIFF(YEAR,@StartDate,@EndDate)
BEGIN
INSERT INTO @Years
SELECT DATEADD(YEAR,DATEDIFF(YEAR,0,DATEADD(YEAR,@I,@StartDate)),0)
SET @I = @I + 1
END
SELECT B.FullName,
B.dob,
DATEADD(YEAR,DATEDIFF(YEAR,dob,Years),dob) BirthDay
FROM @Years A
CROSS JOIN People B
WHERE DATEADD(YEAR,DATEDIFF(YEAR,dob,Years),dob) >= @StartDate
AND DATEADD(YEAR,DATEDIFF(YEAR,dob,Years),dob) <= @EndDate
当然,您不需要每次都创建@Years
表,我建议您创建一个包含此信息的日历表。
结果:
╔════════════╦═════════════════════════╦═════════════════════════╗
║ FullName ║ dob ║ BirthDay ║
╠════════════╬═════════════════════════╬═════════════════════════╣
║ John Smith ║ 1965-02-28 00:00:00.000 ║ 2014-02-28 00:00:00.000 ║
║ Alex Black ║ 1960-02-29 00:00:00.000 ║ 2014-02-28 00:00:00.000 ║
║ Bill Doors ║ 1968-02-27 00:00:00.000 ║ 2014-02-27 00:00:00.000 ║
║ John Smith ║ 1965-02-28 00:00:00.000 ║ 2015-02-28 00:00:00.000 ║
║ Alex Black ║ 1960-02-29 00:00:00.000 ║ 2015-02-28 00:00:00.000 ║
║ Bill Doors ║ 1968-02-27 00:00:00.000 ║ 2015-02-27 00:00:00.000 ║
║ John Smith ║ 1965-02-28 00:00:00.000 ║ 2016-02-28 00:00:00.000 ║
║ Alex Black ║ 1960-02-29 00:00:00.000 ║ 2016-02-29 00:00:00.000 ║
║ Bill Doors ║ 1968-02-27 00:00:00.000 ║ 2016-02-27 00:00:00.000 ║
╚════════════╩═════════════════════════╩═════════════════════════╝
答案 1 :(得分:3)
你可以试试这个
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
编辑:Lamak提醒我一个计算生日的好方法,所以这里的编辑版本
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
答案 2 :(得分:0)
DECLARE @bd DATE = '1960-02-29';
WITH years
AS ( SELECT
*
FROM (VALUES (2013),
(2014),
(2015),
(2016)) AS x(y) )
SELECT
y, DATEADD( year, y - DATEPART( year, @bd ), @bd )
FROM years