Sự khác biệt về tháng giữa các ngày trong MySQL


85

Tôi đang tìm cách tính số tháng giữa 2 trường thời gian ngày tháng.

Có cách nào tốt hơn là lấy dấu thời gian unix và số chia cho 2 592 000 (giây) và làm tròn trong MySQL không?

Câu trả lời:


16

Hàm DATEDIFF có thể cung cấp cho bạn số ngày giữa hai ngày. Cái nào chính xác hơn, vì ... làm thế nào để bạn xác định một tháng? (28, 29, 30 hay 31 ngày?)


68
PERIODDIFF có thể tính toán chính xác số tháng.
Max Caceres

10
"Chính xác hơn" là chủ quan đáng kinh ngạc khi nói đến ngày tháng. Ví dụ, nếu bạn đang kinh doanh trong một doanh nghiệp có chu kỳ hàng tháng, việc biết có bao nhiêu tháng giữa hai ngày có thể quan trọng hơn số ngày vì chính lý do bạn đã nêu ở trên. Một tháng là bao lâu? Nếu tôi cần một tháng đếm tôi có thể không chỉ đơn giản là chia cho 30.
Thomas Paine

6
Đây không phải là câu trả lời chính xác; bạn nên đánh dấu câu trả lời của Max là đúng, bên dưới.
Graham Charles

1
Câu trả lời của Max không đúng nếu bạn muốn số nguyên tháng, tính toán chính xác cho các năm nhuận và số ngày khác nhau trong một tháng. PERIODDIFFchỉ cần lấy giá trị YYYYMM, vì vậy trừ khi bạn tự tính số ngày trước khi vượt qua giá trị YYYYMM, thì tất cả những gì bạn đang tính là số tháng, được làm tròn đến tháng gần nhất nếu có ít hơn một số tháng. Hãy xem câu trả lời của Zane bên dưới.
rgvcorley

2
Tại sao có rất nhiều lượt ủng hộ cho PERIODDIFFnhận xét? Định kỳ không tính giá trị ngày, vì vậy bạn đang tính số tháng được làm tròn đến tháng gần nhất nếu có ít hơn một số tháng
rgvcorley

218

Chênh lệch hàng tháng giữa hai ngày nhất định bất kỳ:

Tôi ngạc nhiên là điều này vẫn chưa được đề cập:

Hãy xem hàm TIMESTAMPDIFF () trong MySQL.

Những gì điều này cho phép bạn làm là chuyển hai TIMESTAMPhoặc DATETIMEgiá trị (hoặc thậm chí DATEkhi MySQL sẽ tự động chuyển đổi) cũng như đơn vị thời gian bạn muốn dựa trên sự khác biệt của mình.

Bạn có thể chỉ định MONTHlàm đơn vị trong tham số đầu tiên:

SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-04')
-- Outputs: 0

SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-05')
-- Outputs: 1

SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-15')
-- Outputs: 1

SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-12-16')
-- Outputs: 7

Về cơ bản, nó lấy số tháng trôi qua kể từ ngày đầu tiên trong danh sách tham số. Giải pháp này tự động bù cho lượng ngày khác nhau trong mỗi tháng (28,30,31) cũng như tính đến các năm nhuận - bạn không phải lo lắng về bất kỳ điều gì trong số đó.


Chênh lệch tháng với độ chính xác:

Sẽ phức tạp hơn một chút nếu bạn muốn giới thiệu độ chính xác thập phân theo số tháng đã trôi qua, nhưng đây là cách bạn có thể làm điều đó:

SELECT 
  TIMESTAMPDIFF(MONTH, startdate, enddate) +
  DATEDIFF(
    enddate,
    startdate + INTERVAL
      TIMESTAMPDIFF(MONTH, startdate, enddate)
    MONTH
  ) /
  DATEDIFF(
    startdate + INTERVAL
      TIMESTAMPDIFF(MONTH, startdate, enddate) + 1
    MONTH,
    startdate + INTERVAL
      TIMESTAMPDIFF(MONTH, startdate, enddate)
    MONTH
  )

Các tham số ngày của bạn ở đâu startdatevà ở đâu enddate, cho dù đó là từ hai cột ngày trong bảng hay dưới dạng tham số đầu vào từ tập lệnh:

Ví dụ:

With startdate = '2012-05-05' AND enddate = '2012-05-27':
-- Outputs: 0.7097

With startdate = '2012-05-05' AND enddate = '2012-06-13':
-- Outputs: 1.2667

With startdate = '2012-02-27' AND enddate = '2012-06-02':
-- Outputs: 3.1935

8
Đây là câu trả lời đúng, tất cả các upvotes cho PERIODDIFFbình luận về câu trả lời được lựa chọn rất bỏ lỡ hàng đầu
rgvcorley

5
Cảm ơn, trong trường hợp của tôi, tôi cần ngày bao gồm, vì vậy tôi thay thế tất cả "enddate" bằng "date_add (enddate, khoảng 1 ngày)". Sau đó, 2014-03-01 đến 2014-05-31 sẽ cho 3,00 thay vì 2,97
Sven Tore

2
Cảm ơn bạn rất nhiều ..... đặc biệt thanx cho "Sự khác biệt giữa các tháng với độ chính xác:". Bạn là ngôi sao của mysql
Vidhi

1
Đã đồng ý. Đây là câu trả lời chính xác. Chính xác những gì tôi đang tìm kiếm! Cảm ơn.
Jon Vote

104

PERIOD_DIFF tính toán các tháng giữa hai ngày.

Ví dụ: để tính toán sự khác biệt giữa now () và một cột thời gian trong your_table:

select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;

1
Bất kỳ ý tưởng nào nó sẽ làm gì khi nó có thể là 11 tháng và 1 ngày, vậy 12 tháng, nhưng nếu bạn thay đổi thành tháng 30 ngày thì nó vẫn là 11?
Darryl Hein

1
11 tháng và 1 ngày sẽ trả về 11 tháng với ví dụ trên. PERIOD_DIFF không có nghĩa là tháng 30 so với 31 ngày.
Max Caceres

26

Tôi cũng sử dụng PERIODDIFF . Để lấy năm và tháng trong ngày, tôi sử dụng hàm EXTRACT :

  SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;

2
Đáng kinh ngạc! Nó sử dụng ít hơn 50% thời gian DATE_FORMATtheo phương pháp đó , cảm ơn! +1
David Rodrigues

1
Đây cho dấu thời gian unix SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time, "%Y%m%d"))) AS months FROM your_table;
Smolla

2
Điều này không chính xác, PERIOD_DIFFkhông tính giá trị ngày, vì vậy bạn đang tính số tháng được làm tròn đến tháng gần nhất nếu có ít hơn một số tháng. Bạn nên chỉnh sửa câu trả lời của mình để làm rõ điều này.
rgvcorley

12

Như nhiều câu trả lời ở đây cho thấy, câu trả lời 'đúng' phụ thuộc vào chính xác những gì bạn cần. Trong trường hợp của tôi, tôi cần làm tròn đến số nguyên gần nhất .

Hãy xem xét những ví dụ sau: Ngày 1 tháng 1 -> ngày 31 tháng 1: Đó là 0 tháng và dài gần 1 tháng. 1 tháng 1 -> 1 tháng 2? Đó là 1 tháng, và đúng 1 tháng.

Để nhận số tháng toàn bộ (đầy đủ) , hãy sử dụng:

SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31');  => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01');  => 1

Để có được khoảng thời gian làm tròn theo tháng, bạn có thể sử dụng:

SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1

Điều này chính xác đến +/- 5 ngày và trong phạm vi hơn 1000 năm. Câu trả lời của Zane rõ ràng là chính xác hơn, nhưng nó quá dài so với ý thích của tôi.


Tôi không đồng ý ... CHỌN TIMESTAMPDIFF (THÁNG, '2018-01-31', '2018-02-04'); cho bạn cách nhau 0 tháng ... CHỌN TIMESTAMPDIFF (THÁNG, '2018-01-31', '2018-03-01'); cho bạn 1 ... nếu 5 ngày là 0, 29 ngày là 1?
Scott

@Scott, có một tháng (được gọi là tháng Hai) từ tháng Giêng đến tháng Ba. TIMESTAMPDIFF chỉ cung cấp số tháng đầy đủ giữa các ngày. Nếu bạn muốn bất kỳ khoảng thời gian theo tháng nào bằng một tháng, hãy sử dụng phiên bản làm tròn mà tôi đã đưa ra.
IanS

8

Từ hướng dẫn sử dụng MySQL:

PERIOD_DIFF (P1, P2)

Trả về số tháng giữa các khoảng thời gian P1 và P2. P1 và P2 phải ở định dạng YYMM hoặc YYYYMM. Lưu ý rằng các đối số dấu chấm P1 và P2 không phải là giá trị ngày tháng.

mysql> CHỌN PERIOD_DIFF (200802,200703); -> 11

Vì vậy, có thể làm một cái gì đó như sau:

Select period_diff(concat(year(d1),if(month(d1)<10,'0',''),month(d1)), concat(year(d2),if(month(d2)<10,'0',''),month(d2))) as months from your_table;

Trong đó d1 và d2 là biểu thức ngày tháng.

Tôi phải sử dụng câu lệnh if () để đảm bảo rằng các tháng là một số có hai chữ số như 02 thay vì 2.


8

Tôi thích cách này hơn, vì ngay từ cái nhìn đầu tiên, evryone sẽ hiểu rõ ràng:

SELECT
    12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
    tab;

Stanislav - phương pháp này chính xác nếu bạn muốn biết dữ liệu THEO SỐ THÁNG. Tôi có một số báo cáo hiển thị doanh số bán hàng theo tháng (tháng 1 / tháng 2 / tháng 3, v.v.) nhưng có quá nhiều dữ liệu (biểu đồ theo quý / năm / các năm trước, v.v.) nên tôi không thể nhóm theo bất kỳ thứ gì. Tôi cần lấy dữ liệu thô và lặp qua nó - khi nó là 1/31, nhiều phép tính đặt "tháng tới" vào tháng 3 khi con người chúng ta biết đó là tháng 2. NẾU nó là 2/4, một số tính toán nói rằng 1/31 là "tháng này" nhưng 1/28 là "tháng trước"
Scott

6

Có cách nào tốt hơn? Đúng. Không sử dụng MySQL Timestamps. Ngoài thực tế là chúng chiếm 36 Byte, chúng không thuận tiện chút nào để làm việc. Tôi khuyên bạn nên sử dụng Ngày và Giây Julian từ nửa đêm cho tất cả các giá trị ngày / giờ. Chúng có thể được kết hợp để tạo thành UnixDateTime. Nếu dữ liệu này được lưu trữ trong DWORD (Số nguyên 4 Byte chưa được đánh dấu) thì các ngày trong khoảng thời gian lên đến 2106 có thể được lưu trữ dưới dạng giây kể từ epoc, 01/01/1970 DWORD max val = 4,294,967,295 - DWORD có thể chứa 136 năm Giây

Julian Dates rất tốt để làm việc khi thực hiện các phép tính ngày Các giá trị UNIXDateTime rất tốt để làm việc khi thực hiện các phép tính Ngày / Giờ Cả hai đều không tốt để xem xét, vì vậy tôi sử dụng Dấu thời gian khi tôi cần một cột mà tôi sẽ không tính toán nhiều với, nhưng tôi muốn có một chỉ dẫn trong nháy mắt.

Chuyển đổi sang Julian và ngược lại có thể được thực hiện rất nhanh chóng bằng một ngôn ngữ tốt. Sử dụng con trỏ, tôi có khoảng 900 Clks (Tất nhiên, đây cũng là một chuyển đổi từ STRING thành INTEGER)

Khi bạn tham gia vào các ứng dụng nghiêm túc sử dụng thông tin Ngày / Giờ, chẳng hạn như thị trường tài chính, ngày tháng Julian là thực tế.


Bạn lấy thông tin ở đâu về thực tế là chúng chiếm 36 byte ? Thủ công MySQL nêu yêu cầu lưu trữ cho cột TIMESTAMP là 4 byte . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
poncha

bắt đầu lập kế hoạch cho lỗi Y2106 ngay bây giờ! ;-)
ErichBSchulz

5

Truy vấn sẽ như sau:

select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..

Cung cấp một số tháng theo lịch kể từ khi dấu dữ liệu được tạo trên hồ sơ khách hàng, cho phép MySQL thực hiện lựa chọn tháng trong nội bộ.


2
DROP FUNCTION IF EXISTS `calcula_edad` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `calcula_edad`(pFecha1 date, pFecha2 date, pTipo char(1)) RETURNS int(11)
Begin

  Declare vMeses int;
  Declare vEdad int;

  Set vMeses = period_diff( date_format( pFecha1, '%Y%m' ), date_format( pFecha2, '%Y%m' ) ) ;

  /* Si el dia de la fecha1 es menor al dia de fecha2, restar 1 mes */
  if day(pFecha1) < day(pFecha2) then
    Set vMeses = VMeses - 1;
  end if;

  if pTipo='A' then
    Set vEdad = vMeses div 12 ;
  else
    Set vEdad = vMeses ;
  end if ;
  Return vEdad;
End

select calcula_edad(curdate(),born_date,'M') --  for number of months between 2 dates

2

Thực thi mã này và nó sẽ tạo ra một hàm dateeifference sẽ cung cấp cho bạn sự khác biệt về định dạng ngày yyyy-mm-dd.

DELIMITER $$

CREATE FUNCTION datedifference(date1 DATE, date2 DATE) RETURNS DATE
NO SQL

BEGIN
    DECLARE dif DATE;
    IF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < 0    THEN
                SET dif=DATE_FORMAT(
                                        CONCAT(
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 , 
                                            '-',
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 , 
                                            '-',
                                            DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(DATE_SUB(date1, INTERVAL 1 MONTH)), '-', DAY(date2))))),
                                        '%Y-%m-%d');
    ELSEIF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < DAY(LAST_DAY(DATE_SUB(date1, INTERVAL 1 MONTH))) THEN
                SET dif=DATE_FORMAT(
                                        CONCAT(
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 , 
                                            '-',
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 , 
                                            '-',
                                            DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
                                        '%Y-%m-%d');
    ELSE
                SET dif=DATE_FORMAT(
                                        CONCAT(
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 , 
                                            '-',
                                            PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 , 
                                            '-',
                                            DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
                                        '%Y-%m-%d');
    END IF;

RETURN dif;
END $$
DELIMITER;

1
Bạn có nghĩ rằng bạn có thể dọn dẹp và nhận xét mã của mình một chút để nó dễ hiểu hơn một chút không? Cảm ơn
Darryl Hein

1

Điều này phụ thuộc vào cách bạn muốn xác định # tháng. Trả lời câu hỏi này: 'Sự khác biệt trong các tháng: 15 tháng 2 năm 2008 - 12 tháng 3 năm 2009'. Nó được xác định bằng số ngày rõ ràng phụ thuộc vào năm nhuận - tháng đó là tháng nào, hoặc cùng ngày của tháng trước = 1 tháng.

Một phép tính cho Ngày :

Ngày 15 tháng 2 -> 29 (năm nhuận) = 14 ngày 1 tháng 3 năm 2008 + 365 = ngày 1 tháng 3 năm 2009. Ngày 1 tháng 3 -> 12 tháng 3 = 12 ngày. 14 + 365 + 12 = 391 ngày. Tổng cộng = 391 ngày / (số ngày trung bình trong tháng = 30) = 13.03333

Cách tính tháng :

Ngày 15 tháng 2 năm 2008 - ngày 15 tháng 2 năm 2009 = 12 ngày 15 tháng 2 -> ngày 12 tháng 3 = dưới 1 tháng Tổng cộng = 12 tháng hoặc 13 nếu ngày 15 tháng 3 - ngày 12 tháng 3 được coi là 'tháng trước'


1
SELECT * 
FROM emp_salaryrevise_view 
WHERE curr_year Between '2008' AND '2009' 
    AND MNTH Between '12' AND '1'

1

Tôi cần sự khác biệt theo tháng với độ chính xác. Mặc dù giải pháp của Zane Bien là đúng hướng, nhưng ví dụ thứ hai và thứ ba của anh ấy cho kết quả không chính xác. Một ngày trong tháng Hai chia cho số ngày trong tháng Hai không bằng một ngày trong tháng Năm chia cho số ngày trong tháng Năm. Vì vậy, ví dụ thứ hai sẽ xuất ra ((31-5 + 1) / 31 + 13/30 =) 1.3043 và ví dụ thứ ba ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701.

Tôi đã kết thúc với truy vấn sau:

SELECT
    '2012-02-27' AS startdate,
    '2012-06-02' AS enddate,
    TIMESTAMPDIFF(DAY, (SELECT startdate), (SELECT enddate)) AS days,
    IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), 0, (TIMESTAMPDIFF(DAY, (SELECT startdate), LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY)) / DAY(LAST_DAY((SELECT startdate)))) AS period1,     
    TIMESTAMPDIFF(MONTH, LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY, LAST_DAY((SELECT enddate))) AS period2,
    IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), (SELECT days), DAY((SELECT enddate))) / DAY(LAST_DAY((SELECT enddate))) AS period3,
    (SELECT period1) + (SELECT period2) + (SELECT period3) AS months

1

Hàm PERIOD_DIFF ()

Một trong những cách là MySQL PERIOD_DIFF () trả về sự khác biệt giữa hai khoảng thời gian. Các khoảng thời gian phải có cùng định dạng, tức là YYYYMM hoặc YYMM. Cần lưu ý rằng dấu chấm không phải là giá trị ngày.

Mã:

SELECT PERIOD_DIFF(200905,200811);

nhập mô tả hình ảnh ở đây


0

Bạn có thể nhận được năm, tháng và ngày theo cách này:

SELECT 
username
,date_of_birth
,DATE_FORMAT(CURDATE(), '%Y') - DATE_FORMAT(date_of_birth, '%Y') - (DATE_FORMAT(CURDATE(), '00-%m-%d') < DATE_FORMAT(date_of_birth, '00-%m-%d')) AS years
,PERIOD_DIFF( DATE_FORMAT(CURDATE(), '%Y%m') , DATE_FORMAT(date_of_birth, '%Y%m') ) AS months
,DATEDIFF(CURDATE(),date_of_birth) AS days
FROM users

0

Bạn cũng có thể thử điều này:

select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;

HOẶC LÀ

select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;

0

Câu trả lời đơn giản cho ngày bắt đầu là ins_frm và ngày kết thúc là ins_to

SELECT convert(TIMESTAMPDIFF(year, ins_frm, ins_to),UNSIGNED) as yrs,
       mod(TIMESTAMPDIFF(MONTH, ins_frm, ins_to),12) mnths
FROM table_name

Thưởng thức :)))


0

Thử đi

SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))

Chào mừng bạn đến với Stack Overflow! Đảm bảo kiểm tra ngày trên các câu hỏi và liệu các câu trả lời hiện có có cùng giải pháp hay không.
lehiester

-1

Truy vấn này phù hợp với tôi :)

SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'

Chỉ cần mất hai ngày và truy xuất các giá trị giữa chúng.


hoàn toàn không chính xác câu trả lời, anh ta muốn đếm tháng
Shane Rowatt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.