Der Unterschied in Monaten zwischen Daten in MySQL

85

Ich möchte die Anzahl der Monate zwischen zwei Datums- und Uhrzeitfeldern berechnen.

Gibt es einen besseren Weg, als den Unix-Zeitstempel und die Division durch 2 592 000 (Sekunden) zu erhalten und innerhalb von MySQL aufzurunden?

Darryl Hein
quelle

Antworten:

16

Mit der Funktion DATEDIFF können Sie die Anzahl der Tage zwischen zwei Daten angeben . Was ist genauer, da ... wie definieren Sie einen Monat? (28, 29, 30 oder 31 Tage?)

Seifenkiste
quelle
68
PERIODDIFF kann die Anzahl der Monate genau berechnen.
Max Caceres
10
"Genauer" ist unglaublich subjektiv, wenn es um Daten geht. Wenn Sie sich beispielsweise in einem Unternehmen mit monatlichen Zyklen befinden und wissen müssen, wie viele Monate zwischen zwei Daten liegen, kann dies aus dem oben genannten Grund wichtiger sein als die Anzahl der Tage. Wie lang ist ein Monat? Wenn ich eine Monatszählung brauche, kann ich nicht einfach durch 30 teilen.
Thomas Paine
6
Dies ist nicht die richtige Antwort. Sie sollten Max ' Antwort unten richtig markieren .
Graham Charles
1
Die Antwort von Max ist nicht richtig, wenn Sie die Anzahl der ganzen Monate, die Schaltjahre und die unterschiedliche Anzahl der Tage in einem Monat korrekt berücksichtigen möchten. PERIODDIFFNimmt einfach einen JJJJMM-Wert an. Wenn Sie also nicht die Tage selbst berücksichtigen, bevor Sie die JJJJMM-Werte übergeben, berechnen Sie nur die Anzahl der Monate, aufgerundet auf den nächsten Monat, wenn weniger als eine ganze Anzahl von Monaten vorhanden ist. Siehe Zanes Antwort unten.
Rgvcorley
2
Warum gibt es so viele positive Stimmen für den PERIODDIFFKommentar? Perioddiff nimmt keine Tageswerte an, daher berechnen Sie die Anzahl der Monate, die auf den nächsten Monat aufgerundet werden, wenn weniger als eine ganze Anzahl von Monaten vorhanden ist
rgvcorley
218

Monatsdifferenz zwischen zwei Daten:

Ich bin überrascht, dass dies noch nicht erwähnt wurde:

Schauen Sie sich die Funktion TIMESTAMPDIFF () in MySQL an.

Auf diese Weise können Sie zwei TIMESTAMPoder mehr DATETIMEWerte (oder sogar, DATEwenn MySQL automatisch konvertiert) sowie die Zeiteinheit übergeben, auf die Sie Ihre Differenz stützen möchten.

Sie können MONTHim ersten Parameter als Einheit angeben :

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

Grundsätzlich wird die Anzahl der Monate angegeben, die seit dem ersten Datum in der Parameterliste vergangen sind. Diese Lösung kompensiert automatisch die unterschiedliche Anzahl von Tagen in jedem Monat (28, 30, 31) sowie die Berücksichtigung von Schaltjahren - Sie müssen sich um nichts davon kümmern.


Monatsunterschied mit Präzision:

Es ist etwas komplizierter, wenn Sie in der Anzahl der verstrichenen Monate eine Dezimalgenauigkeit einführen möchten, aber so können Sie dies tun:

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
  )

Wo startdateund enddatesind Ihre Datumsparameter, sei es aus zwei Datumsspalten in einer Tabelle oder als Eingabeparameter aus einem Skript:

Beispiele:

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
Zane Bien
quelle
8
Dies ist die richtige Antwort, alle positiven Stimmen für den PERIODDIFFKommentar zur ausgewählten Antwort sind sehr
falsch
5
Vielen Dank, in meinem Fall brauchte ich inklusive Daten, daher ersetze ich alle "Enddaten" durch "date_add (Enddatum, Intervall 1 Tag)". Dann 2014-03-01 bis 2014-05-31 geben 3,00 statt 2,97
Sven Tore
2
Vielen Dank ..... besonderer Dank für "Monatsunterschied mit Präzision:". Du bist der Star von MySQL
Vidhi
1
Einverstanden. Dies ist die richtige Antwort. Genau das, wonach ich gesucht habe! Vielen Dank.
Jon Vote
104

PERIOD_DIFF berechnet Monate zwischen zwei Daten.

So berechnen Sie beispielsweise die Differenz zwischen now () und einer Zeitspalte in your_table:

select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;
Max Caceres
quelle
1
Irgendeine Idee, was es macht, wenn es 11 Monate und 1 Tag sein könnte, also 12 Monate, aber wenn Sie auf 30 Tage Monate umsteigen, dann wären es immer noch 11?
Darryl Hein
1
11 Monate und 1 Tag würden mit dem obigen Beispiel 11 Monate zurückgeben. PERIOD_DIFF hat keinen Sinn für 30 vs 31 Tage Monate.
Max Caceres
26

Ich benutze auch PERIODDIFF . Um das Jahr und den Monat des Datums zu erhalten, benutze ich die Funktion EXTRACT :

  SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
Smolla
quelle
2
Unglaublich! Diese DATE_FORMATMethode benötigt 50% weniger Zeit , danke! +1
David Rodrigues
1
Hier für einen Unix-Zeitstempel 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
Dies ist falsch, PERIOD_DIFFnimmt keine Tageswerte an, daher berechnen Sie die Anzahl der auf den nächsten Monat aufgerundeten Monate, wenn weniger als eine ganze Anzahl von Monaten vorhanden ist. Sie sollten Ihre Antwort bearbeiten, um dies zu verdeutlichen.
Rgvcorley
12

Wie viele der Antworten hier zeigen, hängt die richtige Antwort genau davon ab, was Sie brauchen. In meinem Fall muss ich auf die nächste ganze Zahl runden .

Betrachten Sie diese Beispiele: 1. Januar -> 31. Januar: Es sind 0 ganze Monate und fast 1 Monat lang. 1. Januar -> 1. Februar? Es ist 1 ganzer Monat und genau 1 Monat lang.

Um die Anzahl der ganzen (vollständigen) Monate zu erhalten, verwenden Sie:

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

Um eine gerundete Dauer in Monaten zu erhalten, können Sie Folgendes verwenden:

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

Dies ist auf +/- 5 Tage und für Bereiche über 1000 Jahre genau. Zanes Antwort ist offensichtlich genauer, aber für meinen Geschmack zu ausführlich.

IanS
quelle
Ich bin anderer Meinung ... SELECT TIMESTAMPDIFF (MONAT, '2018-01-31', '2018-02-04'); gibt Ihnen 0 Monate auseinander ... SELECT TIMESTAMPDIFF (MONAT, '2018-01-31', '2018-03-01'); gibt Ihnen 1 ... wenn 5 Tage 0 sind, sind 29 Tage 1?
Scott
@Scott, zwischen Januar und März liegt ein ganzer Monat (Februar genannt). TIMESTAMPDIFF gibt nur die Anzahl der vollständigen Monate zwischen den Daten an. Wenn Sie möchten, dass ein Zeitraum von einem Monat einem Monat entspricht, verwenden Sie die von mir angegebene gerundete Version.
IanS
8

Aus dem MySQL-Handbuch:

PERIOD_DIFF (P1, P2)

Gibt die Anzahl der Monate zwischen den Perioden P1 und P2 zurück. P1 und P2 sollten das Format JJJM oder JJJJM haben. Beachten Sie, dass die Periodenargumente P1 und P2 keine Datumswerte sind.

mysql> SELECT PERIOD_DIFF (200802,200703); -> 11

So kann es möglich sein, so etwas zu tun:

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;

Wobei d1 und d2 die Datumsausdrücke sind.

Ich musste die if () - Anweisungen verwenden, um sicherzustellen, dass die Monate eine zweistellige Zahl wie 02 statt 2 waren.

Vincent Ramdhanie
quelle
8

Ich bevorzuge diesen Weg, weil jeder ihn auf den ersten Blick klar verstehen wird:

SELECT
    12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
    tab;
Stanislav Basovník
quelle
Stanislav - Diese Methode ist genau richtig, wenn Sie Daten NACH MONATSNUMMER wissen möchten. Ich habe einige Berichte, die Verkäufe nach Monat (Januar / Februar / März usw.) anzeigen, aber es gibt so viele Daten (Diagramme nach Quartal / Jahr / vergangenen Jahren usw.), dass ich nach nichts gruppieren kann. Ich muss die Rohdaten einlesen und sie durchlaufen - wenn es 1/31 ist, setzen viele der Berechnungen "nächsten Monat" in den März, wenn wir Menschen wissen, dass es Februar ist. WENN es 2/4 ist, sagen einige Berechnungen das 1/31 ist "dieser Monat", aber 1/28 war "letzter Monat"
Scott
6

Gibt es einen besseren Weg? Ja. Verwenden Sie keine MySQL-Zeitstempel. Abgesehen von der Tatsache, dass sie 36 Bytes belegen, ist es überhaupt nicht bequem, mit ihnen zu arbeiten. Ich würde empfehlen, Julian Date und Seconds ab Mitternacht für alle Datums- / Zeitwerte zu verwenden. Diese können zu einer UnixDateTime kombiniert werden. Wenn dies in einem DWORD (vorzeichenlose 4-Byte-Ganzzahl) gespeichert ist, können Daten bis zu 2106 als Sekunden seit dem Epoc gespeichert werden. 01.01.1970 DWORD max val = 4.294.967.295 - Ein DWORD kann 136 Jahre lang halten

Julian Dates sind sehr gut zu bearbeiten, wenn Datumsberechnungen durchgeführt werden. UNIXDateTime-Werte sind gut zu verwenden, wenn Datums- / Uhrzeitberechnungen durchgeführt werden mit, aber ich möchte einen Blick auf einen Blick.

Das Konvertieren zu Julian und zurück kann sehr schnell in einer guten Sprache erfolgen. Mit Zeigern habe ich es auf ungefähr 900 Clks reduziert (dies ist natürlich auch eine Konvertierung von einem STRING zu einem INTEGER).

Wenn Sie in ernsthafte Anwendungen geraten, die Datums- / Uhrzeitinformationen wie beispielsweise die Finanzmärkte verwenden, sind julianische Daten de facto.

Mike Trader
quelle
Woher haben Sie die Informationen darüber , dass sie 36 Bytes belegen ? Das MySQL-Handbuch gibt an, dass der Speicherbedarf für die TIMESTAMP-Spalte 4 Byte beträgt . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
Poncha
Planen Sie jetzt den Y2106-Fehler! ;-)
ErichBSchulz
5

Die Abfrage lautet wie folgt:

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

Gibt eine Anzahl von Kalendermonaten seit dem erstellten Datenstempel in einem Kundendatensatz an, sodass MySQL die Monatsauswahl intern vornehmen kann.

Shubham Verma
quelle
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
Rama
quelle
2

Wenn Sie diesen Code ausführen, wird eine Funktion mit Datumsdifferenz erstellt, die den Unterschied im Datumsformat JJJJ-MM-TT anzeigt.

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;
enor
quelle
1
Denkst du, du könntest deinen Code ein bisschen aufräumen und kommentieren, damit er ein bisschen verständlicher wird? Danke
Darryl Hein
1

Dies hängt davon ab, wie die Anzahl der Monate definiert werden soll. Beantworten Sie diese Fragen: "Was ist der Unterschied in Monaten: 15. Februar 2008 - 12. März 2009". Wird es durch eine eindeutige Anzahl von Tagen definiert, die von den Schaltjahren abhängt - welcher Monat es ist oder der gleiche Tag des Vormonats = 1 Monat.

Eine Berechnung für Tage :

15. Februar -> 29 (Schaltjahr) = 14. März 2008 + 365 = 1. März 2009. 1. März -> 12. März = 12 Tage. 14 + 365 + 12 = 391 Tage. Gesamt = 391 Tage / (durchschnittliche Tage im Monat = 30) = 13.03333

Eine Berechnung der Monate :

15. Februar 2008 - 15. Februar 2009 = 12. Februar 15 -> 12. März = weniger als 1 Monat Gesamt = 12 Monate oder 13, wenn der 15. Februar - 12. März als "letzter Monat" betrachtet wird

Klathzazt
quelle
1
SELECT * 
FROM emp_salaryrevise_view 
WHERE curr_year Between '2008' AND '2009' 
    AND MNTH Between '12' AND '1'
Darryl Hein
quelle
1

Ich brauchte einen Monatsunterschied mit Präzision. Obwohl die Lösung von Zane Bien in die richtige Richtung geht, liefern sein zweites und drittes Beispiel ungenaue Ergebnisse. Ein Tag im Februar geteilt durch die Anzahl der Tage im Februar entspricht nicht einem Tag im Mai geteilt durch die Anzahl der Tage im Mai. Das zweite Beispiel sollte also ((31-5 + 1) / 31 + 13/30 =) 1.3043 und das dritte Beispiel ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701 ausgeben.

Am Ende hatte ich folgende Abfrage:

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
Michel
quelle
1

PERIOD_DIFF () Funktion

Eine der Möglichkeiten ist, dass MySQL PERIOD_DIFF () die Differenz zwischen zwei Perioden zurückgibt. Perioden sollten das gleiche Format haben, dh JJJJMM oder JJMM. Es ist zu beachten, dass Perioden keine Datumswerte sind.

Code:

SELECT PERIOD_DIFF(200905,200811);

Geben Sie hier die Bildbeschreibung ein

Amitesh Bharti
quelle
0

Sie können Jahre, Monate und Tage auf diese Weise erhalten:

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
Artur Kędzior
quelle
0

Sie können dies auch versuchen:

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

ODER

select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;
Shubham Verma
quelle
0

Einfache Antwort mit Startdatum als ins_frm und Enddatum als 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

Genießen :)))

Mo Kassab
quelle
0

Versuche dies

SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))
Maceo
quelle
Willkommen bei Stack Overflow! Stellen Sie sicher, dass Sie das Datum bei Fragen überprüfen und prüfen, ob vorhandene Antworten dieselbe Lösung haben.
Lehiester
-1

Diese Abfrage hat bei mir funktioniert :)

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

Es dauert einfach zwei Daten und ruft die Werte zwischen ihnen ab.

Khaleel Rshid
quelle
völlig falsche Antwort, er will eine Anzahl von Monaten
Shane Rowatt