2009-07-09 41 views
35

tôi cần phải tính toán số lượng ĐẦY ĐỦ tháng trong SQL, ví dụ:Tính số đầy đủ tháng giữa hai ngày trong SQL

  • 2009-04-16 để 2009/05/15 => 0 tháng đầy đủ
  • 2009-04-16 để 2009/05/16 => 1 tháng đầy đủ
  • 2009-04-16 để 2009/06/16 => 2 đầy đủ tháng

tôi cố gắng để sử dụng DATEDIFF, tức là

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

nhưng thay vì đem lại cho tôi đầy đủ tháng từ ngày hai, nó mang lại cho tôi sự khác biệt của phần tháng, ví dụ:

1 

ai biết làm thế nào để tính toán số tháng đầy đủ trong SQL Server?

+5

2009-01-31 đến 2009-02-28. Đó là 0 hay 1 tháng đầy đủ? –

+0

số này phải là 0 – oscarkuo

Trả lời

40

Bài đăng gốc có một số lỗi ... vì vậy tôi đã viết lại và đóng gói dưới dạng 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

Cảm ơn bạn! –

+3

Vâng tôi biết câu trả lời này đã hơn 5 năm rồi nhưng tôi đã bắt gặp nó khi googling. Chỉ có một vấn đề với điều này, nó rơi xuống khi so sánh ngày kết thúc của một tháng. FullMonthsSeparation ('2012-12-31', '2013-02-28') trả về 1, không phải 2. –

+0

Đó là hành vi mà OP yêu cầu. – Barett

-1

DATEDIFF() được thiết kế để trả về các ranh giới số được vượt qua giữa hai ngày cho khoảng thời gian được chỉ định. Để làm cho nó làm những gì bạn muốn, bạn cần phải thực hiện một điều chỉnh bổ sung cho tài khoản khi những ngày qua một ranh giới nhưng không hoàn thành toàn bộ khoảng thời gian.

-2

Tôi googled qua internet. Và gợi ý tôi tìm thấy là thêm +1 vào cuối.

Hãy thử làm điều đó như thế này:

Declare @Start DateTime 
Declare @End DateTime 

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

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

Điều đó sẽ không hiệu quả đối với rất nhiều trường hợp, hãy kiểm tra nó trong lần đầu tiên. –

1

định nghĩa của bạn của một tháng là gì? Về mặt kỹ thuật một tháng có thể là 28,29,30 hoặc 31 ngày tùy theo tháng và năm nhuận.

Có vẻ như bạn đang xem xét một tháng là 30 ngày kể từ trong ví dụ của bạn, bạn bỏ qua rằng tháng có 31 ngày, vậy tại sao không chỉ làm như sau?

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

Tôi nghĩ câu hỏi mà OP muốn trả lời là: "Tôi có thể tăng" tháng "của ngày đầu tiên trước khi vượt qua giây thứ hai?" (với xử lý thích hợp của năm) –

+1

thực tế là OP "bỏ qua rằng có thể có 31 ngày" cho thấy rằng ông đã làm * không * xem xét một tháng là 30 ngày – chiccodoro

1

Hãy thử:

trunc(Months_Between(date2, date1)) 
+1

những gì rdbms sử dụng trunc(), không sql-server? – Taryn

0
SELECT 12 * (YEAR(end_date) - YEAR(start_date)) + 
    ((MONTH(end_date) - MONTH(start_date))) + 
    SIGN(DAY(end_date)/DAY(start_date)); 

này hoạt động tốt đối với tôi trên SQL SERVER 2000.

+0

(MS-SQL 2014) Đối với năm 2009-04-16 đến 2009-05-15, trả về 1 thay vì 0. Trong năm 2009-04-16 đến 2009-05-16, số này trả về 2 thay vì 1. Cho năm 2009 -04-16 đến 2009-06-16 này trả về 3 thay vì 2. Tôi ngạc nhiên rằng có sự khác biệt như vậy giữa SQL 2000 & 2014 – brewmanz

3

này là dành cho ORACLE chỉ và không cho SQL-Server:

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

A nd cho tháng đầy đủ:

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

Có thể được sử dụng trong Oracle 8i trở lên.

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; 
+0

Đối với MS SQL Server – MentalArrow

2

Chức năng dateadd có thể được sử dụng để bù đắp cho sự khởi đầu của tháng. Nếu ngày kết thúc có phần ngày ít hơn startDate, nó sẽ được đẩy vào tháng trước, do đó dateiff sẽ cung cấp số tháng chính xác.

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

Trong khi mã này có thể trả lời câu hỏi, nó sẽ tốt hơn để giải thích _how_ nó giải quyết vấn đề mà không giới thiệu người khác và _why_ để sử dụng nó. Câu trả lời chỉ có mã không hữu ích trong thời gian dài. – JAL

0

Không cần thiết để tạo chức năng chỉ phần @result. Ví dụ:

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

Câu trả lời này tuân theo định dạng T-SQL. Tôi khái niệm hóa vấn đề này là một trong khoảng cách tuyến tính giữa hai điểm ngày ở định dạng datetime, gọi chúng là Time1 và Time2; Time1 nên được căn chỉnh với giá trị 'cũ hơn' thời gian bạn đang xử lý (nói ngày sinh hoặc ngày tạo widget Ngày bắt đầu hành trình) và Time2 phải được căn chỉnh với giá trị 'mới hơn trong thời gian' (nói ngày chụp hoặc ngày hoàn thành tiện ích hoặc ngày đến điểm kiểm tra hành trình).

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

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

Giải pháp này sử dụng phép đo đơn giản, chuyển đổi và tính toán giao điểm nối tiếp của nhiều chu kỳ có độ dài khác nhau; ở đây: Thế kỷ, Thập kỷ, Năm, Tháng, Ngày (Cảm ơn Lịch của người Maya cho khái niệm này!). Lời cảm ơn nhanh chóng: Tôi cảm ơn những người đóng góp khác cho Stack Overflow cho tôi thấy một số chức năng thành phần trong quá trình này mà tôi đã ghép nối với nhau. Tôi đã đánh giá tích cực những điều này trong thời gian của tôi trên diễn đàn này.

Đầu tiên, xây dựng đường chân trời là tập hợp tuyến tính của các giao lộ của các chu kỳ Thế kỷ, Thập phân, Năm, Tháng, gia tăng theo tháng. Sử dụng chéo tham gia chức năng Descartes cho việc này. (Hãy suy nghĩ về điều này như tạo ra vải từ đó chúng ta sẽ cắt một chiều dài giữa các điểm hai 'yyyy-mm' để đo khoảng cách):

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 

Sau đó, chuyển đổi điểm ngày time1 và time2 của bạn vào 'yyyy -mm 'định dạng (Hãy suy nghĩ của những điểm này như là các điểm cắt tọa độ trên toàn bộ vải). Giữ lại bản gốc datetime phiên bản của các điểm cũng như:

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 

Sau đó, chọn khoảng cách thứ tự của các đơn vị 'yyyy-mm, ít một để chuyển đổi khoảng cách hồng y (tức là cắt một mảnh vải từ toàn bộ vải tại các điểm cắt được xác định và được đo thô của nó):

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: tôi gọi đây là một 'khoảng cách thô' vì thành phần tháng của 'yyyy-mm' hồng y khoảng cách có thể là một quá nhiều; các thành phần của chu kỳ ngày trong tháng cần được so sánh để xem liệu giá trị tháng trước có nên tính hay không. Trong ví dụ này, khoảng cách đầu ra thô là '12'. Nhưng điều này sai như 12/14 là trước ngày 12/15, do đó chỉ 11 tháng đã hết hiệu lực - chỉ một ngày rụt rè trong vòng 12 tháng. Do đó, chúng tôi phải đưa vào chu kỳ ngày trong tháng để có được câu trả lời cuối cùng. Chèn một so sánh vị trí 'tháng, ngày' giữa để xác định xem tháng ngày điểm mới nhất đếm trên danh nghĩa, hoặc không:

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: Câu trả lời đúng '11' bây giờ là đầu ra của chúng tôi. Và vì vậy, tôi hy vọng điều này sẽ giúp. Cảm ơn!

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

Tôi nhận thấy đây là một bài đăng cũ, nhưng tôi đã tạo ra giải pháp thú vị mà tôi nghĩ là dễ thực hiện bằng cách sử dụng câu lệnh CASE.

Ước tính sự khác biệt bằng cách sử dụng DATEDIFF và sau đó kiểm tra các tháng trước và sau khi sử dụng DATEADD để tìm ngày tốt nhất. Điều này giả định ngày 31 tháng 1 đến ngày 28 tháng 2 là 1 tháng (vì nó là).

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] 
Các vấn đề liên quan