在 SQL Server 2005 中计算最近的未来周期付款日期
支付阿杰
结论
首次付款日为 2009-06-01。先计算给定日期与首次付款日之间的天数,再按每 14 天一个付款周期取整。
如果 datetime 包含时间,还要比较候选付款时间和给定时间。候选时间已经过去时,应顺延 14 天。
推荐写法
以下写法适用于 SQL Server 2005,也能正确处理 datetime 中的时间部分:
DECLARE @firstPayment datetime;
DECLARE @fromDate datetime;
DECLARE @candidate datetime;
SET @firstPayment = '20090601';
SET @fromDate = '20090620';
IF @fromDate <= @firstPayment
BEGIN
SET @candidate = @firstPayment;
END
ELSE
BEGIN
SET @candidate = DATEADD(
day,
14 * (DATEDIFF(day, @firstPayment, @fromDate) / 14),
@firstPayment
);
IF @candidate < @fromDate
SET @candidate = DATEADD(day, 14, @candidate);
END;
SELECT @candidate AS NextPaymentDate;
当 @fromDate = '20090620' 时,结果是:
2009-06-29 00:00:00.000
当 @fromDate = '20090612' 时,结果是:
2009-06-15 00:00:00.000
如果给定日期恰好是付款日,例如 2009-06-15 00:00:00,则仍然返回 2009-06-15。这符合“当天或之后最近的付款日期”这一要求。
在查询中使用
给定日期保存在变量中时,可以通过 CROSS APPLY 计算候选日期,再将结果用于 WHERE 条件:
DECLARE @firstPayment datetime;
DECLARE @fromDate datetime;
SET @firstPayment = '20090601';
SET @fromDate = '20090620';
SELECT t.*
FROM dbo.YourTable AS t
CROSS APPLY
(
SELECT CASE
WHEN @fromDate <= @firstPayment
THEN @firstPayment
ELSE DATEADD(
day,
14 * (DATEDIFF(day, @firstPayment, @fromDate) / 14),
@firstPayment
)
END AS CandidateDate
) AS c
CROSS APPLY
(
SELECT CASE
WHEN c.CandidateDate < @fromDate
THEN DATEADD(day, 14, c.CandidateDate)
ELSE c.CandidateDate
END AS NextPaymentDate
) AS p
WHERE t.someDate < p.NextPaymentDate;
具体计算分为四步:
- 用
DATEDIFF(day, ...)计算首次付款日与给定日期之间的天数。 - 用整数除法
/ 14计算已经过去的完整付款周期数。 - 用
DATEADD算出当前周期对应的付款日期。 - 如果这个付款时间早于给定时间,就再增加 14 天。
仅处理日期时的简化写法
如果输入不包含时间部分,并且不会早于首次付款日,可以直接写成:
DATEADD(
day,
14 * ((DATEDIFF(day, '20090601', @fromDate) + 13) / 14),
'20090601'
)
例如:
DECLARE @fromDate datetime;
SET @fromDate = '20090620';
SELECT DATEADD(
day,
14 * ((DATEDIFF(day, '20090601', @fromDate) + 13) / 14),
'20090601'
) AS NextPaymentDate;
其中,+ 13 用来实现向上取整:
周期数 = CEILING(相差天数 / 14)
SQL Server 2005 中,两个整数相除时执行整数除法。因此,对于正整数,可以用下面的公式模拟向上取整:
(相差天数 + 13) / 14
注意事项
- 日期常量建议写成
'20090601'这样的YYYYMMDD格式,避免受到 SQL Server 会话语言和DATEFORMAT设置的影响。 - 简化公式只适用于不早于首次付款日的日期。如果输入可能早于
2009-06-01,应使用CASE直接返回首次付款日。 - 如果输入包含时间,例如
2009-06-15 10:00:00,那么当天的付款时间2009-06-15 00:00:00已经过去,下一次付款应为2009-06-29 00:00:00。此时应使用推荐写法,不能只按天数套用向上取整公式。 - 如果业务要求返回“严格晚于给定日期”的付款时间,即输入恰好等于付款时刻时也要返回下一周期,应将相关比较由
<改为<=。
备注:内容仅供参考。
