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中的完整月份数?
原始帖子有一些错误……所以我重新编写并将其打包为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
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
dateadd函数可用于偏移到月初。 如果endDate的天部分少于startDate,则它将被推到前一个月,因此datediff将给出正确的月数。
DATEDIFF(MONTH, DATEADD(DAY,-DAY(startDate)+1,startDate),DATEADD(DAY,-DAY(startDate)+1,endDate))
这仅适用于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及更高版本中使用。
您对一个月的定义是什么? 从技术上讲,一个月可以是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
DATEDIFF()用于返回指定日期范围的两个日期之间相交的数字边界。 为了使它能够执行您想要的操作,您需要进行其他调整以考虑日期何时跨越边界但没有完成整个跨度。
SELECT 12 * (YEAR(end_date) - YEAR(start_date)) +
((MONTH(end_date) - MONTH(start_date))) +
SIGN(DAY(end_date) / DAY(start_date));
这对我在SQL SERVER 2000上工作正常。
尝试:
trunc(Months_Between(date2, date1))
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;
只需创建@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;
该答案遵循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”是我们的输出。 因此,我希望这会有所帮助。 谢谢!
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))
我意识到这是一篇老文章,但是我创建了这个有趣的解决方案,我认为使用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]
简单易行,只需将此完整代码复制并粘贴到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
我通过互联网搜索。我发现的建议是在末尾加上+1。
尝试这样做:
Declare @Start DateTime
Declare @End DateTime
Set @Start = '11/1/07'
Set @End = '2/29/08'
Select DateDiff(Month, @Start, @End + 1)