整理四個高頻日期計算標量函數,採用集合思維與日期邊界運算,無循環、無游標、高性能,可直接部署至生產庫供查詢、存儲過程、計算列調用。
CREATE FUNCTION dbo.DaysInMonth (@dt DATETIME)
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
-- 核心邏輯:下月 1 號 - 1 天 = 本月最後一天
RETURN DAY(DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @dt) + 1, 0)));
END;
GO
-- 調用
SELECT dbo.DaysInMonth('2024-02-15'); -- 返回 29 (閏年)
SELECT dbo.DaysInMonth('2023-02-15'); -- 返回 28
原理解析:DATEDIFF(MONTH, 0, @dt) 獲取從 1900-01 到目標月的月份差 → +1 定位下月 → DATEADD(MONTH, ..., 0) 獲得下月 1 號 00:00 → DATEADD(DAY, -1, ...) 回退一天 → DAY() 取日期部分即天數。
-- 方法一:基準日期 1900-01-01 (周一)
SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19000101', GETDATE()), '19000101');
-- 方法二:基準 0 (1900-01-01 隱式轉換)
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, GETDATE()), 0);
-- 函數封裝
CREATE FUNCTION dbo.MondayOfWeek (@dt DATETIME)
RETURNS DATE
WITH SCHEMABINDING
AS
BEGIN
RETURN DATEADD(WEEK, DATEDIFF(WEEK, 0, @dt), 0);
END;
注意:SQL Server 預設 @@DATEFIRST 決定週起始 (美式=7 周日,歐式=1 周一)。上述函數強制以周一為起始,跨語言環境穩定。
SELECT DATEADD(QUARTER, DATEDIFF(QUARTER, 0, GETDATE()), 0);
-- 返回 2024-10-01 00:00:00.000 (假設當前 Q4)
CREATE FUNCTION dbo.FirstDayOfQuarter (@dt DATETIME)
RETURNS DATE
WITH SCHEMABINDING
AS
BEGIN
RETURN DATEADD(QUARTER, DATEDIFF(QUARTER, 0, @dt), 0);
END;
CREATE FUNCTION dbo.DaysInQuarter (@dt DATETIME)
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
DECLARE @QuarterStart DATE = DATEADD(QUARTER, DATEDIFF(QUARTER, 0, @dt), 0);
RETURN DATEDIFF(DAY, @QuarterStart, DATEADD(QUARTER, 1, @QuarterStart));
END;
GO
-- 等價內聯邏輯 (無需函數)
DECLARE @QuarterStart DATE = DATEADD(QUARTER, DATEDIFF(QUARTER, 0, GETDATE()), 0);
SELECT DATEDIFF(DAY, @QuarterStart, DATEADD(QUARTER, 1, @QuarterStart)); -- 返回 92 (Q4)
| 函數類型 | 特點 | 建議 |
|---|---|---|
| 標量函數 (上述) | 易用、可 SCHEMABINDING、但大結果集逐行調用有函數開銷 | 小結果集、計算列、WHERE 子句簡單值 |
| 內聯表值函數 (iTVF) | 優化器內聯展開、零開銷、可並行 | 大批量查詢、JOIN、APPLY 場景首選 |
| 多語句表值函數 (mTVF) | 邏輯複雜、但有表變量開銷、不內聯 | 極複雜邏輯無法單語句表達時 |