單純用 `DATEDIFF(YEAR, birth, today)` 只計算「年份界線跨越次數」,忽略月日是否已過,導致生日未到卻多算一歲。本文提供生產級標量函數,處理閏年 2/29、跨世紀閏年規則、負年齡修正等完整邊界條件。
CREATE FUNCTION dbo.Get_FullAge
(
@Birthday DATETIME,
@CurrentDay DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @Age INT;
-- 1. 粗略年差 (跨越幾次 1/1 界線)
SET @Age = DATEDIFF(YEAR, @Birthday, @CurrentDay);
-- 2. 精修:若今年生日尚未到,減 1
-- 原理:把出生日期加上粗略年差,得到「今年的生日」,若大於今天,代表還沒過生日
IF DATEDIFF(DAY, DATEADD(YEAR, @Age, @Birthday), @CurrentDay) < 0
SET @Age = @Age - 1;
-- 3. 閏年特例:2/29 出生者,非閏年 3/1 視為生日
-- 若出生 2/29,且今天是非閏年 3/1,DATEDIFF 會誤判為已過生日,需再減 1
IF DATEPART(MONTH, @Birthday) = 2
AND DATEPART(DAY, @Birthday) = 29
AND DATEPART(MONTH, @CurrentDay) = 3
AND DATEPART(DAY, @CurrentDay) = 1
AND NOT (YEAR(@CurrentDay) % 4 = 0 AND (YEAR(@CurrentDay) % 100 != 0 OR YEAR(@CurrentDay) % 400 = 0))
SET @Age = @Age - 1;
-- 4. 防禦:出生日期在未來 (髒資料) 回傳 0
IF @Age < 0
SET @Age = 0;
RETURN @Age;
END;
DATEDIFF(DAY, ..., @CurrentDay) < 0 等價於「今年生日 > 今天」。| 寫法 | 缺陷 | 適用場景 |
|---|---|---|
DATEDIFF(YEAR, birth, GETDATE()) | 生日未到多算 1 歲 | 粗略統計、不嚴謹報表 |
FLOOR(DATEDIFF(DAY, birth, GETDATE()) / 365.25) | 閏年平均近似,邊界仍有誤差 | 近似估算 |
| 本文函數 | 無 (完整處理邊界) | 精準業務:保險、醫療、會員等級、法定年齡 |
函數為標量 UDF,大量資料查詢時會逐行執行,影響效能。高頻查詢建議: