MySQL'de tarihler arasındaki ay farkı


86

2 tarih ve saat alanı arasındaki ay sayısını hesaplamak istiyorum.

Unix zaman damgasını alıp 2 592 000 (saniye) bölüp MySQL içinde yuvarlamaktan daha iyi bir yol var mı?

Yanıtlar:


16

DATEDIFF işlevi, iki tarih arasındaki gün için size numarasını verebilirim. Hangisi daha doğrudur, çünkü ... bir ayı nasıl tanımlarsınız? (28, 29, 30 veya 31 gün?)


68
PERIODDIFF , ay sayısını doğru bir şekilde hesaplayabilir.
Max Caceres

10
"Daha doğru", tarihler söz konusu olduğunda inanılmaz derecede özneldir. Örneğin, iki tarih arasında kaç ay olduğunu bilmeye ihtiyaç duyan aylık döngülere sahip bir işyerindeyseniz, yukarıda belirttiğiniz nedenden dolayı gün sayısından daha önemli olabilir. Bir ay ne kadar sürer? Bir aylık sayıma ihtiyacım olursa 30'a
bölemem

6
Bu doğru cevap değil; Max'in cevabını aşağıda doğru olarak işaretlemelisiniz .
Graham Charles

1
Tam ay sayısını, artık yılları ve bir aydaki farklı gün sayısını doğru hesaplamak istiyorsanız, Max'in cevabı doğru değildir. PERIODDIFFsadece bir YYYYMM değeri alır, bu nedenle YYYYMM değerlerini geçmeden önceki günleri hesaba katmazsanız, hesapladığınız tek şey ay sayısıdır ve tam sayıdan az ay varsa en yakın aya yuvarlanır. Aşağıdaki Zane'nin cevabına bakın.
rgvcorley

2
PERIODDIFFYorum için neden bu kadar çok olumlu oy var ? Perioddiff gün değerlerini almaz, bu nedenle tam sayıdan daha az ay varsa en yakın aya yuvarlanan ayların sayısını
hesaplıyorsunuz

220

Verilen herhangi iki tarih arasındaki ay farkı:

Bundan henüz bahsedilmemesine şaşırdım:

MySQL'deki TIMESTAMPDIFF () işlevine bir göz atın .

Bunun yapmanıza izin verdiği şey, iki TIMESTAMPveya DATETIMEdeğer (veya DATEMySQL'in otomatik olarak dönüştüğü gibi) ve farkınızı temel almak istediğiniz zaman birimini aktarmaktır.

MONTHİlk parametrede birim olarak belirtebilirsiniz :

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

Temel olarak parametre listesindeki ilk tarihten itibaren geçen ay sayısını alır. Bu çözüm, her ayın değişen gün sayısını (28,30,31) otomatik olarak telafi eder ve artık yılları da hesaba katar - bunlardan hiçbiri için endişelenmenize gerek yoktur.


Hassasiyetle ay farkı:

Geçen ay sayısında ondalık kesinlik eklemek istiyorsanız bu biraz daha karmaşıktır, ancak bunu şu şekilde yapabilirsiniz:

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
  )

Bir tablodaki iki tarih sütunundan veya bir koddan girdi parametreleri olarak tarih parametreleriniz nerede startdateve nerede?enddate

Örnekler:

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
Bu doğru cevap, PERIODDIFFseçilen cevapla ilgili yorum için tüm olumlu oylar çok
yanlış

5
Teşekkürler, benim durumumda kapsayıcı tarihlere ihtiyacım vardı, bu yüzden tüm "enddate" i "date_add (enddate, interval 1 day)" ile değiştirdim. Ardından 2014-03-01'den 2014-05-31'e 2,97 yerine 3,00 verecek
Sven Tore

2
Çok teşekkür ederim ..... "Hassas ay farkı" için özel teşekkürler. You are the star of mysql
Vidhi

1
Kabul. Bu doğru cevap. Tam olarak aradığım şey! Teşekkürler.
Jon

105

PERIOD_DIFF , iki tarih arasındaki ayları hesaplar.

Örneğin, now () ile tablonuzdaki bir zaman sütunu arasındaki farkı hesaplamak için:

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

1
11 ay 1 gün, yani 12 ay, ancak 30 gün aya geçerseniz, o zaman hala 11 olurken ne yaptığı hakkında bir fikriniz var mı?
Darryl Hein

1
11 ay ve 1 gün, yukarıdaki örnekle 11 aya döner. PERIOD_DIFF'in 30'a karşı 31 günlük ay anlamı yoktur.
Max Caceres

27

Ayrıca PERIODDIFF kullanıyorum . Tarihin yılını ve ayını almak için EXTRACT işlevini kullanıyorum :

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

2
İnanılmaz! Bu DATE_FORMATyöntemi % 50 daha az kullanır , teşekkürler! +1
David Rodrigues

1
İşte bir unix zaman damgası için 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
Bu yanlıştır, PERIOD_DIFFgün değerlerini almaz, bu nedenle tam sayıdan daha az ay varsa en yakın aya yuvarlanmış ay sayısını hesaplıyorsunuz. Bunu netleştirmek için cevabınızı düzenlemelisiniz.
rgvcorley

12

Buradaki cevapların çoğunun gösterdiği gibi, 'doğru' cevap tam olarak neye ihtiyacınız olduğuna bağlıdır. Benim durumumda, en yakın tam sayıya yuvarlamam gerekiyor .

Şu örnekleri düşünün: 1 Ocak -> 31 Ocak: 0 tam ay ve neredeyse 1 ay sürüyor. 1 Ocak -> 1 Şubat? Tam 1 ay ve tam olarak 1 ay sürüyor.

Tam (tam) ayların sayısını elde etmek için şunu kullanın:

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

Ay olarak yuvarlatılmış bir süre elde etmek için şunları kullanabilirsiniz:

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

Bu +/- 5 güne kadar ve 1000 yılı aşan aralıklar için doğrudur. Zane'nin cevabı açıkça daha doğru, ama benim beğenime göre fazla ayrıntılı.


Katılmıyorum ... SELECT TIMESTAMPDIFF (MONTH, '2018-01-31', '2018-02-04'); size 0 ay arayla ... SELECT TIMESTAMPDIFF (MONTH, '2018-01-31', '2018-03-01'); 1 verir ... 5 gün 0, 29 gün 1?
Scott

@Scott, Ocak ve Mart arasında tam bir ay (Şubat olarak adlandırılır) vardır. TIMESTAMPDIFF yalnızca tarihler arasındaki tam ayların sayısını verir. Herhangi bir ay büyüklüğündeki dönemin bir aya eşit olmasını istiyorsanız, verdiğim yuvarlak sürümü kullanın.
IanS

8

MySQL kılavuzundan:

PERIOD_DIFF (P1; P2)

P1 ve P2 dönemleri arasındaki ay sayısını döndürür. P1 ve P2, YYMM veya YYYYMM formatında olmalıdır. P1 ve P2 dönem bağımsız değişkenlerinin tarih değerleri olmadığını unutmayın.

mysql> SEÇİM SÜRESİ_DIFF (200802,200703); -> 11

Bu yüzden şöyle bir şey yapmak mümkün olabilir:

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;

D1 ve d2, tarih ifadeleridir.

Ayların 2 yerine 02 gibi iki basamaklı bir sayı olduğundan emin olmak için if () ifadelerini kullanmak zorunda kaldım.


8

Bu yolu tercih ederim, çünkü herkes ilk bakışta bunu açıkça anlayacaktır:

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

Stanislav - AY NUMARASINA GÖRE verileri bilmek istiyorsanız bu yöntem tam olarak doğrudur. Aylara göre satışları gösteren bazı raporlarım var (Ocak / Şubat / Mart vb.) Ancak o kadar çok veri var (çeyrek / yıl / geçmiş yıllara göre grafikler vb.) Hiçbir şeye göre gruplayamıyorum. Ham veriyi almam ve içinden geçmem gerekiyor - 1/31 olduğunda, hesaplamaların çoğu, biz insanlar bunun Şubat olduğunu bildiklerinde "gelecek ayı" Mart ayına koyuyor. 2/4 ise, bazı hesaplamalar şunu söylüyor: 1/31 "bu ay", ancak 1/28 "geçen ay" oldu
Scott

6

Daha iyi bir yol var mı? Evet. MySQL Zaman Damgalarını kullanmayın. 36 Byte'ı işgal etmelerinin yanı sıra, çalışmak için hiç de uygun değiller. Tüm tarih / saat değerleri için gece yarısından itibaren Julian Tarih ve Saniyelerini kullanmanızı tavsiye ederim. Bunlar bir UnixDateTime oluşturmak için birleştirilebilir. Bu bir DWORD'de (işaretsiz 4 Bayt Tamsayı) depolanırsa, epoc, 01/01/1970 DWORD max val = 4,294,967,295'ten bu yana 2106'ya kadar olan tarihler saniyeler olarak saklanabilir - Bir DWORD 136 yıllık Saniyeyi tutabilir

Jülyen Tarihleri, tarih hesaplamaları yaparken çalışmak çok güzel Tarih / Saat hesaplamaları yaparken UNIXDateTime değerleri ile çalışmak iyidir İkisine de bakılması iyi değildir, bu yüzden çok fazla hesaplama yapmayacağım bir sütuna ihtiyacım olduğunda Zaman Damgalarını kullanırım ile, ancak bir bakışta gösterge istiyorum.

Jülyen'e ve geri dönüş, iyi bir dilde çok hızlı bir şekilde yapılabilir. İşaretçileri kullanarak bunu yaklaşık 900 Clks'ye düşürdüm (Bu aynı zamanda STRING'den INTEGER'a bir dönüşüm tabii ki)

Örneğin finans piyasaları gibi Tarih / Saat bilgilerini kullanan ciddi uygulamalara girdiğinizde, Jülyen tarihleri ​​fiilen geçerlidir.


36 bayt işgal ettikleri gerçeğiyle ilgili bilgiyi nereden aldınız ? MySQL manuel, TIMESTAMP sütunu için depolama gereksiniminin 4 bayt olduğunu belirtir . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
poncha

Y2106 hatası için şimdi plan yapmaya başlayın! ;-)
ErichBSchulz

5

Sorgu şöyle olacaktır:

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

Bir müşteri kaydında oluşturulan tarih damgasından bu yana, MySQL'in dahili olarak ay seçimini yapmasına izin vererek birkaç takvim ayı verir.


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

Bu kodu çalıştırın ve size yyyy-aa-gg tarih biçiminde fark verecek bir işlev tarih farkı yaratacaktır.

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
Biraz daha anlaşılır olması için kodunuzu biraz temizleyip yorumlayabileceğinizi düşünüyor musunuz? Teşekkürler
Darryl Hein

1

Bu, # ayın nasıl tanımlanmasını istediğinize bağlıdır. Şu soruları yanıtlayın: 'Aylarda fark nedir: 15 Şubat 2008 - 12 Mart 2009'. Artık yıllara bağlı olan net kesim sayısı ile mi tanımlanır - hangi ay olduğu veya önceki ayın aynı günü = 1 ay.

Günler için bir hesaplama :

15 Şubat -> 29 (artık yıl) = 14 Mart 1, 2008 + 365 = 1 Mart 2009. 1 Mart -> 12 Mart = 12 gün. 14 + 365 + 12 = 391 gün. Toplam = 391 gün / (aydaki ortalama gün sayısı = 30) = 13.03333

Ayların hesaplanması :

15 Şubat 2008 - 15 Şubat 2009 = 12 Şubat 15 -> 12 Mart = 1 aydan az Toplam = 12 ay veya 15 Şubat - 12 Mart 'geçen ay' olarak kabul edilirse 13


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

1

Kesinlikle ay farkına ihtiyacım vardı. Zane Bien'in çözümü doğru yönde olsa da, ikinci ve üçüncü örnekleri yanlış sonuçlar veriyor. Şubat ayındaki bir günün, Şubat ayındaki gün sayısına bölünmesi, Mayıs ayındaki bir günün Mayıs ayındaki gün sayısına bölünmesiyle elde edilen değere eşit değildir. Yani ikinci örnek ((31-5 + 1) / 31 + 13/30 =) 1.3043 ve üçüncü örnek ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701 çıktısı vermelidir.

Şu sorgu ile sonlandırdım:

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

PERIOD_DIFF () işlevi

Yollardan biri MySQL PERIOD_DIFF () iki nokta arasındaki farkı döndürmesidir. Dönemler aynı formatta olmalıdır, yani YYYYMM veya YYMM. Periyotların tarih değerleri olmadığına dikkat edilmelidir.

Kod:

SELECT PERIOD_DIFF(200905,200811);

görüntü açıklamasını buraya girin


0

Yılları, ayları ve günleri şu şekilde elde edebilirsiniz:

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

Ayrıca şunu da deneyebilirsiniz:

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

VEYA

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

0

Başlangıç ​​tarihi ins_frm ve bitiş tarihi ins_to olarak verilen basit cevap

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

Zevk almak :)))


0

Bunu dene

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

Stack Overflow'a hoş geldiniz! Sorulardaki tarihi ve mevcut cevapların aynı çözüme sahip olup olmadığını kontrol ettiğinizden emin olun.
lehiester

-1

Bu sorgu benim için çalıştı :)

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

Sadece iki tarih alır ve aralarındaki değerleri alır.


tamamen yanlış cevap, birkaç ay istiyor
Shane Rowatt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.