如何获得 SQL 中周期性月份的最后一天?

How can I get the last day of periodic months in SQL?

我正在根据我写的查询中给定的值准备下一个 5 个月的日期。

 DECLARE @StartDate DATETIME = '2022-03-31', @monthadd INT = 5;
 ; WITH dates AS (
 
   SELECT @StartDate [vade]
   UNION ALL
   SELECT DATEADD(MONTH,1,[vade])
   FROM  dates
   WHERE DATEADD(MONTH,1,[vade]) <= DATEADD(MONTH,@monthadd,@StartDate)
 )
 SELECT * 
 FROM  dates 
 OPTION (MAXRECURSION 0)
 GO

但是,当月的最后一天为31时,需要列出后面月份的最后一天,也就是最近的一天。我该怎么做?

实际结果

vade
2022-03-31 00:00:00.000
2022-04-30 00:00:00.000
2022-05-30 00:00:00.000
2022-06-30 00:00:00.000
2022-07-30 00:00:00.000
2022-08-30 00:00:00.000

编辑:

这是一个成熟度计划。如果此人在每月 31 日分期付款,则必须在每个月的最后一天付款。如果他在30日做,这个月有30天就应该有30天,有31天就应该有30天,有29天就应该有29天。如果从 20 日开始到期,则必须是每个月的 20 日。假设您在当月 30 号贷款。如果月份是29天,他们会要求你在第29天付款,如果月份是31天,他们会要求你在第30天付款。我知道这很令人困惑,我对此感到抱歉。

2022-04-01 更新

如果我没理解错的话,您想 return 每个月的“日期”相同 - 除了 @StartDate 是该月的最后一天.

一种方法是确定@StartDate 是否是该月的最后一天。如果是这样,请使用 EOMONTH() 到 return 随后每个月的最后一天。否则,使用 DATEADD() return 每个月指定的“天”。这种方法适用于任何日期。

一种方法如下:

  1. 如果到期日是一个月的最后一天,或者到期日月是 > 下个月的天数,使用EOMONTH()到return该月的最后一天
  2. 否则,使用 DATEADD() 和 DATEFROMPARTS() 使用月度 的到期日
  3. 生成下一个日期

SQL:

-- Note: Using 12 months for demo only
; WITH dates AS (
  SELECT @StartDate AS MaturityDate
         , IIF(@StartDate = EOMONTH(@StartDate), 1, 0) AS IsEOM
  UNION ALL
  SELECT 
         CASE -- Maturity date is last day of month OR 
              -- Maturity "day" is > number of days in current month
              WHEN IsEOM = 1 OR DAY(@StartDate) > DAY( EOMONTH(NextMaturityDate) ) 
                    THEN EOMONTH( DATEADD(MONTH, 1, MaturityDate ))
              -- Otherwise, maturity "day" is valid for current month
              ELSE DATEFROMPARTS(
                         Year(NextMaturityDate)
                         , Month(NextMaturityDate)
                         , DAY(@StartDate)
                      )
         END
         , IsEOM
  FROM  ( SELECT MaturityDate
                 , IsEOM
                 , DATEADD(MONTH, 1, MaturityDate) AS NextMaturityDate
          FROM  dates
        ) t
  WHERE MaturityDate < @EndDate
)
SELECT MaturityDate AS [vade] 
FROM   dates 
OPTION (MAXRECURSION 0)

2022-03-31 的结果 (每月最后一天)

vade
2022-03-31
2022-04-30
2022-05-31
2022-06-30
2022-07-31
2022-08-31
2022-09-30
2022-10-31
2022-11-30
2022-12-31
2023-01-31
2023-02-28
2023-03-31
2023-04-30
2023-05-31
2023-06-30

2022 年 3 月 30 日的结果 (不是月份的最后一天)

vade
2022-03-30
2022-04-30
2022-05-30
2022-06-30
2022-07-30
2022-08-30
2022-09-30
2022-10-30
2022-11-30
2022-12-30
2023-01-30
2023-02-28
2023-03-30
2023-04-30
2023-05-30
2023-06-30

db<>fiddle here

DATEADD function已经考虑到了月末等极端情况,所以你不需要处理。

为了拥有更简洁的代码,您可以放置​​一个 stored procedure,创建(或替换)一个 dates_list table,然后循环数月以添加到开始日期。

DELIMITER //

CREATE OR REPLACE PROCEDURE create_dates_list (
    IN start_date DATETIME, 
    IN num_months INT
)
BEGIN
    DECLARE idx INT DEFAULT 0;

    CREATE OR REPLACE TABLE dates_list (
        date DATE
    );

    WHILE idx <> num_months DO
        INSERT INTO tab VALUES(
            DATEADD(@start_date, INTERVAL @idx MONTH)
        );
        SET idx = idx + 1;
    END WHILE;
END //

DELIMITER ;

当您需要获取新的日期时,您可以通过设置参数和调用存储过程来刷新table:

DECLARE @StartDate DATETIME = '2022-03-31', @monthadd INT = 5;

CALL create_dates_list(@StartDate, @monthadd);

您可以随时使用 sql 赋予您的工具自由访问 table。

如果您不需要 table 用于进一步的会话,您可以将 table 定义为 TEMPORARY。临时 table 上的 official documentation 示例非常详细和全面,请查看以了解更多信息。