sql server-计算SQL中两个日期之间的完整月份数

我需要计算SQL中的完整月数,即

  • 2009-04-16至2009-05-15 => 0整个月
  • 2009-04-16至2009-05-16 => 1个月
  • 2009-04-16至2009-06-16 => 2个月

我尝试使用DATEDIFF,即

SELECT DATEDIFF(MONTH, '2009-04-16', '2009-05-15')

但是没有给我两个日期之间的完整月份,而是给了我月份部分的差额,即

1

有谁知道如何计算SQL Server中的完整月份数?

oscarkuo asked 2020-02-23T01:21:40Z
15个解决方案
48 votes

原始帖子有一些错误……所以我重新编写并将其打包为UDF。

CREATE FUNCTION FullMonthsSeparation 
(
    @DateA DATETIME,
    @DateB DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @Result INT

    DECLARE @DateX DATETIME
    DECLARE @DateY DATETIME

    IF(@DateA < @DateB)
    BEGIN
        SET @DateX = @DateA
        SET @DateY = @DateB
    END
    ELSE
    BEGIN
        SET @DateX = @DateB
        SET @DateY = @DateA
    END

    SET @Result = (
                    SELECT 
                    CASE 
                        WHEN DATEPART(DAY, @DateX) > DATEPART(DAY, @DateY)
                        THEN DATEDIFF(MONTH, @DateX, @DateY) - 1
                        ELSE DATEDIFF(MONTH, @DateX, @DateY)
                    END
                    )

    RETURN @Result
END
GO

SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-05-15') as MonthSep -- =0
SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-05-16') as MonthSep -- =1
SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-06-16') as MonthSep -- =2
Joe Davis answered 2020-02-23T01:21:49Z
6 votes
select case when DATEPART(D,End_dATE) >=DATEPART(D,sTAR_dATE) 
THEN ( case when DATEPART(M,End_dATE) = DATEPART(M,sTAR_dATE) AND DATEPART(YYYY,End_dATE) = DATEPART(YYYY,sTAR_dATE) 
        THEN 0 ELSE DATEDIFF(M,sTAR_dATE,End_dATE)END )
ELSE DATEDIFF(M,sTAR_dATE,End_dATE)-1 END
Shankar answered 2020-02-23T01:22:04Z
5 votes

dateadd函数可用于偏移到月初。 如果endDate的天部分少于startDate,则它将被推到前一个月,因此datediff将给出正确的月数。

DATEDIFF(MONTH, DATEADD(DAY,-DAY(startDate)+1,startDate),DATEADD(DAY,-DAY(startDate)+1,endDate))
Jason Brady answered 2020-02-23T01:22:25Z
4 votes

这仅适用于ORACLE,不适用于SQL-Server:

months_between(to_date ('2009/05/15', 'yyyy/mm/dd'), 
               to_date ('2009/04/16', 'yyyy/mm/dd'))

整整一个月:

round(months_between(to_date ('2009/05/15', 'yyyy/mm/dd'), 
                     to_date ('2009/04/16', 'yyyy/mm/dd')))

可以在Oracle 8i及更高版本中使用。

Stephan Schielke answered 2020-02-23T01:22:53Z
3 votes

您对一个月的定义是什么? 从技术上讲,一个月可以是28、29、30或31天,具体取决于月份和leap年。

似乎您正在考虑将30个月作为一个月,因为在您的示例中您忽略了May的31天,那么为什么不这样做呢?

SELECT DATEDIFF(DAY, '2009-04-16', '2009-05-15')/30
    , DATEDIFF(DAY, '2009-04-16', '2009-05-16')/30
    , DATEDIFF(DAY, '2009-04-16', '2009-06-16')/30
MyItchyChin answered 2020-02-23T01:23:18Z
0 votes

DATEDIFF()用于返回指定日期范围的两个日期之间相交的数字边界。 为了使它能够执行您想要的操作,您需要进行其他调整以考虑日期何时跨越边界但没有完成整个跨度。

Joel Coehoorn answered 2020-02-23T01:23:38Z
0 votes
SELECT 12 * (YEAR(end_date) - YEAR(start_date)) +
    ((MONTH(end_date) - MONTH(start_date))) +
    SIGN(DAY(end_date) / DAY(start_date));

这对我在SQL SERVER 2000上工作正常。

Weksley answered 2020-02-23T01:23:58Z
0 votes

尝试:

trunc(Months_Between(date2, date1))
Prosserc answered 2020-02-23T01:24:18Z
0 votes
WITH   
-- Count how many months must be added to @StartDate to exceed @DueDate  
MONTHS_SINCE(n, [Month_hence], [IsFull], [RemainingDays] ) AS (  
SELECT   
    1 as n,  
    DATEADD(Day, -1, DATEADD(Month, 1, @StartDate)) AS Month_hence  
    ,CASE WHEN (DATEADD(Day, -1, DATEADD(Month, 1, @StartDate)) <= @LastDueDate)   
        THEN 1   
        ELSE 0   
    END  AS [IsFull]  
    ,DATEDIFF(day, @StartDate,  @LastDueDate) as [RemainingDays]  
UNION ALL  
SELECT  
    n+1,  
    --DateAdd(Month, 1, Month_hence) as Month_hence -- No, causes propagation of short month discounted days  
    DATEADD(Day, -1, DATEADD(Month, n+1, @StartDate)) as Month_hence  
    ,CASE WHEN (DATEADD(Day, -1, DATEADD(Month, n+1, @StartDate)) <= @LastDueDate)   
        THEN 1   
        ELSE 0    
    END  AS [IsFull]  
    ,DATEDIFF(day, DATEADD(Day, -1, DATEADD(Month, n, @StartDate)),  @LastDueDate)  
    FROM MONTHS_SINCE   
    WHERE Month_hence<( @LastDueDate --WHERE Period= 1  
    )  
), --SELECT * FROM MONTHS_SINCE  
MONTH_TALLY (full_months_over_all_terms, months_over_all_terms, days_in_incomplete_month ) AS (  
SELECT  
    COALESCE((SELECT MAX(n) FROM MONTHS_SINCE WHERE isFull = 1),1) as full_months_over_all_terms,  
    (SELECT MAX(n) FROM MONTHS_SINCE ) as months_over_all_terms,  
    COALESCE((SELECT [RemainingDays] FROM MONTHS_SINCE WHERE isFull = 0),0) as days_in_incomplete_month  
) SELECT * FROM MONTH_TALLY;   
MentalArrow answered 2020-02-23T01:24:33Z
0 votes

只需创建@result部分就不必创建函数。 例如:

Select Name,
(SELECT CASE WHEN 
DATEPART(DAY, '2016-08-28') > DATEPART(DAY, '2016-09-29')   
THEN DATEDIFF(MONTH, '2016-08-28',  '2016-09-29') - 1
ELSE DATEDIFF(MONTH, '2016-08-28',  '2016-09-29') END) as NumberOfMonths

FROM 
tableExample;
Adrian answered 2020-02-23T01:24:53Z
0 votes

该答案遵循T-SQL格式。 我将这个问题概念化为日期时间格式中两个日期点之间的线性时间距离之一,将它们称为Time1和Time2。 Time1应该与您要处理的“较早的时间”值(例如出生日期或小部件创建日期或旅程的开始日期)对齐,而Time2应该与“较新的时间”值(例如快照日期)对齐 或小部件完成日期或到达旅程检查点的日期)。

DECLARE @Time1 DATETIME
SET @Time1 = '12/14/2015'

DECLARE @Time2 DATETIME
SET @Time2 = '12/15/2016'

该解决方案利用简单的测量,转换和计算不同长度的多个循环的串行交点的能力。 此处:世纪,十年,年,月,日(感谢玛雅日历的概念!)。 快速感谢:感谢Stack Overflow的其他贡献者,向我展示了我在此过程中缝合的一些组件功能。 我在这个论坛上对我的评价很高。

首先,构造一个地平线,该地平线是Century,Decade,Year,Month周期的交点的线性集合,按月递增。 为此使用交叉联接笛卡尔函数。 (以创建布料为例,我们将在布料上切割两个“ yyyy-mm”点之间的长度以测量距离):

SELECT 
Linear_YearMonths = (centuries.century + decades.decade + years.[year] + months.[Month]),
1 AS value
INTO #linear_months
FROM
(SELECT '18' [century] UNION ALL
SELECT '19' UNION ALL
SELECT '20') centuries 
CROSS JOIN 
(SELECT '0' [decade] UNION ALL
SELECT '1' UNION ALL
SELECT '2' UNION ALL
SELECT '3' UNION ALL
SELECT '4' UNION ALL
SELECT '5' UNION ALL
SELECT '6' UNION ALL
SELECT '7' UNION ALL
SELECT '8' UNION ALL
SELECT '9') decades 
CROSS JOIN 
(SELECT '1' [year] UNION ALL
SELECT '2' UNION ALL
SELECT '3' UNION ALL
SELECT '4' UNION ALL
SELECT '5' UNION ALL
SELECT '6' UNION ALL
SELECT '7' UNION ALL
SELECT '8' UNION ALL
SELECT '9' UNION ALL
SELECT '0') years 
CROSS JOIN  
(SELECT '-01' [month] UNION ALL
SELECT '-02' UNION ALL
SELECT '-03' UNION ALL
SELECT '-04' UNION ALL
SELECT '-05' UNION ALL
SELECT '-06' UNION ALL
SELECT '-07' UNION ALL
SELECT '-08' UNION ALL
SELECT '-09' UNION ALL
SELECT '-10' UNION ALL
SELECT '-11' UNION ALL
SELECT '-12') [months]
ORDER BY 1

然后,将您的Time1和Time2日期点转换为'yyyy-mm'格式(可以将它们视为整块布料上的坐标切割点)。 还要保留点的原始日期时间版本:

SELECT
Time1 = @Time1,
[YYYY-MM of Time1] = CASE
WHEN LEFT(MONTH(@Time1),1) <> '1' OR MONTH(@Time1) = '1'
    THEN (CAST(YEAR(@Time1) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time1) AS VARCHAR))
    ELSE (CAST(YEAR(@Time1) AS VARCHAR) + '-' + CAST(MONTH(@Time1) AS VARCHAR))
    END,
Time2 = @Time2,
[YYYY-MM of Time2] = CASE
WHEN LEFT(MONTH(@Time2),1) <> '1' OR MONTH(@Time2) = '1'
    THEN (CAST(YEAR(@Time2) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time2) AS VARCHAR))
    ELSE (CAST(YEAR(@Time2) AS VARCHAR) + '-' + CAST(MONTH(@Time2) AS VARCHAR))
    END
INTO #datepoints

然后,选择“ yyyy-mm”单位的顺序距离,减去一个即可转换为基本距离(即,在确定的切割点从整块布上切下一块布,并进行原始测量):

SELECT 
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM #linear_months l
            WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
FROM #datepoints d

原始输出:之所以称其为“原始距离”,是因为“ yyyy-mm”基数距离的月份成分可能过多。 需要比较月份中的日周期组成部分,以查看是否应该计算上一个月份的值。 具体地,在该示例中,原始输出距离为“ 12”。 但这错误是因为12/14在12/15之前,因此仅过去了整整11个月的时间-距第12个月仅一天的时间。 因此,我们必须引入月内日周期以得出最终答案。 在之间插入“月,日”排名比较,以确定最近的日期点月份是否名义上重要:

SELECT 
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM AZ_VBP.[MY].[edg_Linear_YearMonths] l
            WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
        + (CASE WHEN DAY(Time1) < DAY(Time2)
                THEN -1
                ELSE 0
                END)
FROM #datepoints d

最终输出:现在,正确的答案为“ 11”是我们的输出。 因此,我希望这会有所帮助。 谢谢!

E.Gannon answered 2020-02-23T01:25:44Z
0 votes
select CAST(DATEDIFF(MONTH, StartDate, EndDate) AS float) -
  (DATEPART(dd,StartDate) - 1.0) / DATEDIFF(DAY, StartDate, DATEADD(MONTH, 1, StartDate)) +
  (DATEPART(dd,EndDate)*1.0 ) / DATEDIFF(DAY, EndDate, DATEADD(MONTH, 1, EndDate))
Ren Yuzhi answered 2020-02-23T01:25:59Z
0 votes

我意识到这是一篇老文章,但是我创建了这个有趣的解决方案,我认为使用CASE语句很容易实现。

使用DATEDIFF估算差值,然后使用DATEADD测试前后的月份以找到最佳日期。 假设1月31日至2月28日为1个月(因为是)。

DECLARE @First date = '2015-08-31'
DECLARE @Last date = '2016-02-28'

SELECT
    @First as [First],
    @Last as [Last],
    DateDiff(Month, @First, @Last) as [DateDiff Thinks],
    CASE
        WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) +1, @First) <= @Last Then DATEDIFF(Month, @First, @Last) +1
        WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) , @First) <= @Last Then DATEDIFF(Month, @First, @Last) 
        WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) -1, @First) <= @Last Then DATEDIFF(Month, @First, @Last) -1
    END as [Actual Months Apart]
kevro answered 2020-02-23T01:26:24Z
0 votes

简单易行,只需将此完整代码复制并粘贴到MS SQL并执行:

declare @StartDate date='2019-01-31'
declare @EndDate date='2019-02-28'


SELECT

DATEDIFF(MONTH, @StartDate, @EndDate)+

(

case 

when format(@StartDate,'yyyy-MM') != format(@EndDate,'yyyy-MM') AND DATEPART(DAY,@StartDate) > DATEPART(DAY,@EndDate) AND DATEPART(DAY,@EndDate) = DATEPART(DAY,EOMONTH(@EndDate)) then 0

when format(@StartDate,'yyyy-MM') != format(@EndDate,'yyyy-MM') AND DATEPART(DAY,@StartDate) > DATEPART(DAY,@EndDate)  then -1 

else 0 

end

) 

as NumberOfMonths
Shehan Silva answered 2020-02-23T01:26:44Z
-2 votes

我通过互联网搜索。我发现的建议是在末尾加上+1。

尝试这样做:

Declare @Start DateTime
Declare @End DateTime

Set @Start = '11/1/07'
Set @End = '2/29/08'

Select DateDiff(Month, @Start, @End + 1)
Dmitris answered 2020-02-23T01:27:08Z
translate from https://stackoverflow.com:/questions/1106945/calculating-number-of-full-months-between-two-dates-in-sql