计算SQL中两个日期之间的完整月数

时间:2009-07-09 23:22:44

标签: sql sql-server

我需要在SQL中计算 FULL 月的数量,即

  • 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中的整月数量?

21 个答案:

答案 0 :(得分:47)

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

答案 1 :(得分:5)

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

答案 2 :(得分:4)

dateadd函数可用于偏移到月初。如果endDate的日期部分小于startDate,它将被推送到上个月,因此datediff将给出正确的月数。

DATEDIFF(MONTH, DATEADD(DAY,-DAY(startDate)+1,startDate),DATEADD(DAY,-DAY(startDate)+1,endDate))

答案 3 :(得分:3)

仅适用于 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及更高版本中使用。

答案 4 :(得分:2)

你对一个月的定义是什么?从技术上讲,一个月可能是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

答案 5 :(得分:0)

对上述功能进行一些更改对我有用。

创建函数 [dbo].[FullMonthsSeparation] ( @DateA 日期时间, @DateB 日期时间 ) 退货积分 作为 开始 声明@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) - iif(EOMONTH(@DateY) = @DateY, 0, 1)
                    ELSE DATEDIFF(MONTH, @DateX, @DateY)
                END
                )

RETURN @Result

结束

答案 6 :(得分:0)

我知道这是一个老问题,但是只要日期是> = 1753年1月1日,我就会使用:

var express = require('express');
var bodyParser = require('body-parser')
var app = express();

app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())

app.get('/', (req, res) => {
  res.sendFile(__dirname + './index.html')
})

app.post("/name", (req, res) => {
    let fullName = req.body.first + ' ' + req.body.last;
    res.json({ name: fullName }) 
});

答案 7 :(得分:0)

CREATE FUNCTION ufFullMonthDif (@dStart DATE, @dEnd DATE)
RETURNS INT
AS
BEGIN
    DECLARE @dif INT,
            @dEnd2 DATE
    SET @dif = DATEDIFF(MONTH, @dStart, @dEnd)
    SET @dEnd2 = DATEADD (MONTH, @dif, @dStart)
    IF @dEnd2 > @dEnd
        SET @dif = @dif - 1
    RETURN @dif
END
GO

SELECT dbo.ufFullMonthDif ('2009-04-30', '2009-05-01')
SELECT dbo.ufFullMonthDif ('2009-04-30', '2009-05-29')
SELECT dbo.ufFullMonthDif ('2009-04-30', '2009-05-30')
SELECT dbo.ufFullMonthDif ('2009-04-16', '2009-05-15')
SELECT dbo.ufFullMonthDif ('2009-04-16', '2009-05-16')
SELECT dbo.ufFullMonthDif ('2009-04-16', '2009-06-16')
SELECT dbo.ufFullMonthDif ('2019-01-31', '2019-02-28')

答案 8 :(得分:0)

SELECT dateadd(dd,number,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) AS gun FROM master..spt_values
WHERE type = 'p'
AND year(dateadd(dd,number,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)))=year(DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))

答案 9 :(得分:0)

您可以创建此函数来计算两个日期之间的绝对差。 正如我使用DATEDIFF内置系统功能所发现的那样,我们只会在数月,数日和数年内获得差异。例如:假设有两个日期18-Jan-2018和15-Jan-2019。因此,这些日期之间的差值将由DATEDIFF以12个月的月份给出,而实际上是11个月28天。因此,使用下面给出的函数,我们可以找到两个日期之间的绝对差。

public File createPdfFromTxt(String content) throws IOException, DocumentException {

        //define the size of the PDF file, version and output file
        File outfile = File.createTempFile("mehr", ".pdf");
        Document pdfDoc = new Document(PageSize.A4);
        PdfWriter.getInstance(pdfDoc, new FileOutputStream(outfile)).setPdfVersion(PdfWriter.PDF_VERSION_1_7);
        pdfDoc.open();

        //define the font and also the command that is used to generate new paragraph
        Font myfont = new Font();
        myfont.setStyle(Font.NORMAL);
        myfont.setSize(11);
        pdfDoc.add(new Paragraph("\n"));

        // add paragraphs into newly created PDF file
        File contentFile = File.createTempFile("content", ".txt");
        FileUtils.writeStringToFile(contentFile, content);

        BufferedReader br = new BufferedReader(new FileReader(contentFile));
        String strLine;
        while ((strLine = br.readLine()) != null) {
            Paragraph para = new Paragraph(strLine + "\n", myfont);
            para.setAlignment(Element.ALIGN_JUSTIFIED);
            pdfDoc.add(para);
        }
        pdfDoc.close();
        br.close();

        return outfile;
    }

答案 10 :(得分:0)

您需要做的就是如果结束日期尚未超过开始日期中的月份,则减去额外的月份。

order: {number}

答案 11 :(得分:0)

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

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

选择

DATEDIFF(MONTH,@StartDate,@EndDate)+

案例

当format(@ StartDate,'yyyy-MM')!= format(@ EndDate,'yyyy-MM')AND DATEPART(DAY,@ StartDate)> DATEPART(DAY,@ EndDate)和DATEPART(DAY,@ EndDate)= DATEPART(DAY,EOMONTH(@EndDate))然后为0

当format(@ StartDate,'yyyy-MM')!= format(@ EndDate,'yyyy-MM')AND DATEPART(DAY,@ StartDate)> DATEPART(DAY,@ EndDate)时为-1

其他0

结束

为NumberOfMonths

答案 12 :(得分: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]

答案 13 :(得分: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))

答案 14 :(得分:0)

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

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

然后,将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

Raw Output: 我称之为“原始距离”,因为'yyyy-mm'基数距离的月份成分可能太多了;需要比较一个月内的日期周期组件,以查看上个月的值是否应该计算。具体而言,在该示例中,原始输出距离是“12”。但是12/14的这个错误是在12月15日之前,所以因此只有11个月的时间已经过去了 - 只有一天因为第12个月的失败而感到羞涩。因此,我们必须引入月内的一天周期才能得出最终答案。在\ n之间插入'月,日'位置比较,以确定最新日期点月份是否在名义上计算:

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'的正确答案现在是我们的输出。所以,我希望这会有所帮助。谢谢!

答案 15 :(得分: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;

答案 16 :(得分: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;   

答案 17 :(得分: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。

答案 18 :(得分:0)

尝试:

trunc(Months_Between(date2, date1))

答案 19 :(得分:-1)

DATEDIFF()用于返回指定范围的两个日期之间的数字边界。为了让它做你想做的事情,你需要做一个额外的调整,以考虑日期越过边界但没有完成整个跨度。

答案 20 :(得分:-2)

我在互联网上搜索了一下。 我发现的建议是在结尾添加+1。

尝试这样做:

Declare @Start DateTime
Declare @End DateTime

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

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