2009-07-09 865 views
35

我需要计算FULL个月在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全文的月数?

+5

2009-01-31至2009-02-28。这是0或1个整月吗? – 2009-07-09 23:26:55

+0

这应该是0 – oscarkuo 2009-07-09 23:48:53

回答

40

原来的文章有一些错误...所以我重新编写并将其打包为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 
+0

谢谢你先生! – 2012-10-26 11:55:44

+3

是的,我知道这个答案现在已经超过5年了,但是当我在Google上搜索时发现了它。这只有一个问题,比较一个月的结束日期时会下降。 FullMonthsSeparation('2012-12-31','2013-02-28')返回1,而不是2. – 2015-01-12 16:08:24

+0

这是OP要求的行为。 – Barett 2015-06-05 21:14:33

-1

DATEDIFF()旨在返回指定范围的两个日期之间交叉的数字边界。为了让它做你想做的事情,你需要做一个额外的调整来说明日期跨越边界但是​​没有完成整个跨度的时间。

-2

我使用互联网搜索。 我发现的建议是在最后加上+1。

尝试做这样的:

Declare @Start DateTime 
Declare @End DateTime 

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

Select DateDiff(Month, @Start, @End + 1) 
+0

这对很多情况都不适用,请在第一张照片上查看。 – 2009-07-09 23:49:43

1

什么是你一个月的定义是什么?技术上,一个月可以是28,29,30或31天,具体取决于月份和闰年。

看来你正在考虑一个月为30天,因为在你的例子中,你忽视了5月份有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 
1

尝试:

trunc(Months_Between(date2, date1)) 
0
SELECT 12 * (YEAR(end_date) - YEAR(start_date)) + 
    ((MONTH(end_date) - MONTH(start_date))) + 
    SIGN(DAY(end_date)/DAY(start_date)); 

这工作得很好,我对SQL Server 2000中

3

这是ORACLE,而不是用于SQL-服务器:

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

A nd for full month:

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

可用于Oracle 8i及以上版本。

3
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 
0
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; 
2

DateAdd函数可用于抵消了月初。如果endDate的一天的部分少于startDate,它将被推到前一个月,因此datediff会给出正确的月份数。

datediff(month, dateadd(day,-day(startDate)+1,start),dateadd(day,-day(startDate)+1,endDate)) 
0

不需要创建只有@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; 
0

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

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

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

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

首先,构建一个水平线,它是世纪,十年,年,月的交点的线性集合,按月递增。为此,使用交叉连接笛卡尔函数。 (认为​​这是创造,从中我们将为了切两“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 

然后,将您的时间1和时间2日点到“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 

Raw Output: 我称这是一个“原始的距离”,因为“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 

Final Output: “11”的正确答案是,现在我们的产量。所以,我希望这有助于。谢谢!

0
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)) 
0

我意识到这是一个老帖子,但我创造了这个有趣的解决方案,我认为是很容易使用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] 
相关问题