3

我有一张员工生日表。我正在尝试创建一个存储过程,在 2 个给定日期内返回每个人的生日。我们有闰年出生的员工。

按照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 Black2014 年的生日显示2/28/2014为 2016 年的生日2/29/2016

另外,如果你有心情,我的全部意图如下:

我想通过 2 个日期,无论相距多远:@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
4

3 回答 3

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 ║
╚════════════╩═════════════════════════╩═════════════════════════╝
于 2013-06-28T19:57:09.380 回答
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

看一下 SQL FIDDLE 示例

编辑: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

SQL 提琴示例

于 2013-06-28T19:56:00.210 回答
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 
于 2013-06-28T21:00:43.877 回答