# [MySQL] Alle Monate zwischen Anfangs- und Enddatum auslesen



## 27apricot (14. März 2006)

Hallo,

es geht um eine Tabelle in der jeder Eintrag ein Anfangs- und ein Enddatum hat. Ich möchte als Navigation die Monate (mit Jahr) ausgeben, in denen etwas passiert. Das funktioniert so:

```
SELECT *,
	YEAR(`datum_anfang`) AS 'jahr',
	MONTH(`datum_anfang`), MONTH(`datum_ende`) AS 'monat'
FROM `kal_daten`
GROUP BY `monat`, `jahr`
ORDER BY `datum`;
```
Nun möchte ich aber auch einen eventuell zwischen den zwei Daten liegenden Monat mit ausgeben. Wenn also ein Ereignis beispielsweise von Januar bis März geht, soll auch der Februar als Monat ausgegeben werden. Geht das? Und wenn ja, wie?

Danke schonmal im Voraus:
27apricot


----------



## Ronax (15. März 2006)

Auf die Gefahr hin etwas völlig falsches zu sagen. Ich hatte mal so etwas ähnliches gemacht. Es scheint als ob die Spalten vom Typ Datetime wären. Versuch' es vielleicht mal mit INT und einem Timestamp und dann halt alles in das Ergebnis, was sich z.B. in diesem Jahr befindet. Dann erst bei der eigentlich Ausgabe so ablaufen, dass wenn immer ein neuer Monat beginnt er ausgegeben wird.

Als Beispiel, wenn in diesem Monat etwas passiert, so wird er ausgegeben (PHP):

```
SELECT datum FROM event WHERE datum <= '{$end}' AND datum  >= '{$start}'"
```


```
$start = mktime(0,0,0,$month,1,$year); 
$end  =	mktime(0,0,0,($month)+1,1,$year);
```

wobei du bei $end auch anstatt $month+1 

```
date("t", mktime(0,0,0,$month,1,$year));
```
benutzen könntest

dann die Ausgabe (vereinfacht):

```
foreach( $result as $qry ) {
[...]
$events[date("FY",$qry['datum'])]['Name'] = $mNames[date("m",$qry['datum'])-1] . date(" Y",$qry['datum']);
$events[date("FY",$qry['datum'])]['Monat'] = date("m",$qry['datum']);		
$events[date("FY",$qry['datum'])]['Jahr'] = date("Y",$qry[datum]);	
[...]
			};
```
wobei $mNames die Monatsnamen enthält

und dann zum Beispiel:

```
foreach( $events as $Wert)
			{	
					print $Wert['Name'];
					print $Wert['Anzahl'];
			}
```

hmm...auch wenn das jetzt vielleicht nicht mit übergreifenden (anfang und ende) Datumsangaben funktioniert, ging es vielleicht doch ein wenig in die Richtung.  

viele Grüße
 Florian


----------



## hpvw (15. März 2006)

Was nicht in der Datenbank steht, wirst Du auch nicht aus der Datenbank auslesen können, egal ob Du das Datum als INT (meine Meinung hierzu sollte im Forum mittlerweile bekannt sein ) oder DATE speicherst.

Du müsstest Dein Resultset also im Query "aufbohren", indem Du Dir irgendwie 12 Datensätze mit den Monaten erzeugst, damit irgendwie Joinst oder mit Subqueries arbeitest.
Eine konkrete Idee fällt mir dazu aber ad hoc nicht ein.

Wesentlich schneller geht es vermutlich, wenn Du einfach die Monate von datum_anfang und datum_ende als monat_anfang und monat_ende projezierst und in PHP eine kurze Schleife von monat_anfang bis monat_ende laufen lässt, um die dazwischen liegenden in das Ergebnis aufzunehmen.

Gruß hpvw


----------



## Ronax (15. März 2006)

hpvw hat gesagt.:
			
		

> das Datum als INT (meine Meinung hierzu sollte im Forum mittlerweile bekannt sein ) oder DATE speicherst.


Dann klär mich mal auf, was ist wann besser?


----------



## hpvw (15. März 2006)

Wenn es nach mir geht, immer DATE, DATETIME, TIMESTAMP, TIME oder YEAR, aber es gibt andere Meinungen, die bereits diskutiert wurden.

Gruß hpvw

EDIT: Was mir noch einfällt: Bei der oben angesprochenen Schleife musst Du natürlich auf den Jahreswechsel achten.


----------



## ManicMarble (16. März 2006)

Ich habe mir für solche Geschichten (z.B. Ausgabe aller Monate) eine Tabelle erstellt, die für die gesamte Unix-Epoche einen Datensatz für jeden Tag enthält.
Damit kann ich jegliche Art von Abfragen über Tage, Wochen, Monate, Jahre machen und die zugehörigen Daten dazu-joinen. Da ich viel mit Projektmanagement zu tun habe, ist das Ding für mich inzwischen unentbehrlich. Hab da auch noch hilfreiche Zusatzdaten mit drin, z.B. alle Feiertage mit Namen, ausgeschriebene Datums-Angaben, Wochentag, Kalenderwoche, was man halt so braucht.

Ich stelle gerne einen Dump zur Verfügung, wenn gewünscht. Oder hier das PHP-Script, das diese Tabelle erzeugt:


```
<?php 
	
	function ostern($id) {
		
		$y = date("Y", $id);
		$a = $y % 19;
		$b = $y % 4;
		$c = $y % 7;
		$d = (19 * $a + 24) % 30;
		$e = (2 * $b + 4 * $c + 6 * $d + 5) % 7;
		
		$oD = 22 + $d + $e;
		$oM = 3;
		if ($oD > 31) {
			$oD = $d + $e - 9;
			$oM = 4;
		}
		
		if ($oD == 26 && $oM == 4) $oD = 19;
		if ($oD == 25 && $oM == 4 && $d == 28 && $e == 6 && $a > 10) $oD = 18;
		
		return mktime(0, 0, 0, $oM, $oD, $y);
	}
	
	
	function feiertag($id) {
		
		if (date("d.m", $id) == "01.01") return "Neujahr";
		if (date("d.m", $id) == "06.01") return "Dreikönig";
		if (date("d.m", $id) == "01.05") return "Tag der Arbeit";
		if (date("d.m", $id) == "03.10") return "Tag der deutschen Einheit";
		if (date("d.m", $id) == "01.11") return "Allerheiligen";
		if (date("d.m", $id) == "24.12") return "(Heilig Abend)";
		if (date("d.m", $id) == "25.12") return "1. Weihnachtstag";
		if (date("d.m", $id) == "26.12") return "2. Weihnachtstag";
		if (date("d.m", $id) == "31.12") return "(Sylvester)";
		
		$oS = ostern($id);
		
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) +  2, date("Y", $id)) == $oS) return "Karfreitag";
		if (mktime(0, 0, 0, date("n", $id), date("j", $id)     , date("Y", $id)) == $oS) return "Ostersonntag";
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) -  1, date("Y", $id)) == $oS) return "Ostermontag";
		
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) - 39, date("Y", $id)) == $oS) return "Christi Himmelfahrt";
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) - 49, date("Y", $id)) == $oS) return "Pfingstsonntag";
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) - 50, date("Y", $id)) == $oS) return "Pfingstmontag";
		if (mktime(0, 0, 0, date("n", $id), date("j", $id) - 60, date("Y", $id)) == $oS) return "Fronleichnam";
		
		return "";
	}

	
	// DB-Verbindung:
	$con = bls_dbConnect("intranet");
	
	// Tabelle ggf. löschen:
	mysql_query("DROP TABLE IF EXISTS kalender", $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
	
	// Tabelle anlegen:
	$sql = "
		CREATE TABLE kalender (
		  id int(11) unsigned NOT NULL default '0',
		  datum date NOT NULL default '0000-00-00',
		  tag int(2) unsigned NOT NULL default '0',
		  wtag int(1) unsigned NOT NULL default '0',
		  wtag17 int(1) unsigned NOT NULL default '0',
		  wtagname char(10) NOT NULL default '',
		  wtagkurz char(2) NOT NULL default '',
		  kw int(2) unsigned NOT NULL default '0',
		  kwjahr int(4) unsigned NOT NULL default '0',
		  monat int(2) unsigned NOT NULL default '0',
		  monatname char(9) NOT NULL default '',
		  monatkurz char(3) NOT NULL default '',
		  jahr int(4) unsigned NOT NULL default '0',
		  jahrkurz int(2) unsigned NOT NULL default '0',
		  datumganzlang varchar(32) NOT NULL default '',
		  arbeitstag char(1) NOT NULL default '',
		  feiertag char(32) NOT NULL default '',
		  arbh_n int(2) unsigned NOT NULL default '0',
		  arbh_2 int(2) unsigned NOT NULL default '0',
		  arbh_3 int(2) unsigned NOT NULL default '0',
		  arbh_v int(2) unsigned NOT NULL default '0',
		  PRIMARY KEY  (id),
		  UNIQUE KEY datum (datum)
		) TYPE=MyISAM
	";
	mysql_query($sql, $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
	
	$kwjahr = 1970;
	
	// Tage von 1971 bis 2036:
	for($i = mktime(0,0,0,1,1,1971); $i <= mktime(0,0,0,12,31,2036); $i += 86400) {
		
		$id = $i - (date("I", $i) * 3600); // Sommerzeit: 1 Std. abziehen
		
		$datum = date("Y-m-d", $id);
		
		$tag = date("j", $id);
		
		$wtag = date("w", $id);
		$wtag17 = ($wtag == 0) ? 7 : $wtag;
		
		$wtagname = array("Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag");
		$wtagname = $wtagname[$wtag];
		
		$wtagkurz = array("So","Mo","Di","Mi","Do","Fr","Sa");
		$wtagkurz = $wtagkurz[$wtag];
		
		$kw = strftime("%V", $id);
		
		$kwjahr = date("Y", $id);
		if ($kw == "01" && date("n", $id) == 12) $kwjahr += 1;
		if ($kw >= "25" && date("n", $id) == 1)  $kwjahr -= 1;
		
		if ($kw == "01") $kwjahr = (date("n", $id) == 1) ? date("Y", $id) : date("Y", $id)+1;
		
		$monat = date("n", $id);
		
		$monatname = array("Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember");
		$monatname = $monatname[$monat-1];
		
		$monatkurz = array("Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez");
		$monatkurz = $monatkurz[$monat-1];
		
		$jahr = date("Y", $id);
		$jahrkurz = date("y", $id);
		
		$datumganzlang = $wtagname . ", " . $tag . ". " . $monatname . " " . $jahr;
		
		$feiertag = feiertag($id);
		
		$arbeitstag = "A";
		if ($wtag == 5) $arbeitstag = "a";
		
		//letzter Sa. vor Hl. Abend:
		$xtag = mktime(0, 0, 0, 12, 24, $jahr);
		while (date("w", $xtag) != 6) $xtag -= 86400;
		if ($id >= $xtag) $arbeitstag = "=";
		
		// 3-König:
		$xtag = mktime(0, 0, 0, 1, 6, $jahr);
		if ($id < $xtag) $arbeitstag = "=";
		
		if ($wtag == 0 || $wtag == 6) $arbeitstag = "-";
		if (  !(($feiertag == "") || (substr($feiertag, 0, 1) == "("))  ) $arbeitstag = "*";
		
		$arbh_n = ($arbeitstag == "A") ?  8 : (($arbeitstag == "a") ?  5 : 0);
		$arbh_2 = ($arbeitstag == "A") ? 15 : (($arbeitstag == "a") ?  9 : 0);
		$arbh_3 = ($arbeitstag == "A") ? 23 : (($arbeitstag == "a") ? 15 : 0);
		$arbh_v = ($arbeitstag == "A") ? 24 : (($arbeitstag == "a") ? 24 : 0);
		
		$sql = "
			INSERT INTO kalender SET 
			id='$id',
			datum='$datum',
			tag='$tag',
			wtag='$wtag',
			wtag17='$wtag17',
			wtagname='$wtagname',
			wtagkurz='$wtagkurz',
			kw='$kw',
			kwjahr='$kwjahr',
			monat='$monat',
			monatname='$monatname',
			monatkurz='$monatkurz',
			jahr='$jahr',
			jahrkurz='$jahrkurz',
			datumganzlang='$datumganzlang',
			arbeitstag='$arbeitstag',
			feiertag='$feiertag',
			arbh_n='$arbh_n',
			arbh_2='$arbh_2',
			arbh_3='$arbh_3',
			arbh_v='$arbh_v'
		";
		
		echo $id.": ".date("d.m.Y", $id)." - KW$kw/$kwjahr $arbeitstag&nbsp;&nbsp;$wtagname, $tag. $monatname $jahr".(($arbeitstag == "*") ? ", $feiertag" : "");
		
		mysql_query($sql, $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
		
		echo "<br>";
	}
	
	// Änderung 12.1.2006: Ab 1. August 2005 neue Tageskapazitäten (40h-Woche):

	echo "&nbsp;<br>&nbsp;<br>Anpassung der Tageskapazitäten auf 40h-Woche...";
	
	// Mo bis Do:
	$sql = "
		UPDATE 
		  `kalender`
		SET
		  `arbh_n` = '8.75',
		  `arbh_2` = '16.25',
		  `arbh_3` = '23.25'
		WHERE
			  BINARY `arbeitstag` = BINARY 'A'
		  AND `datum` >= '2005-08-01'	
	";
	mysql_query($sql, $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
	
	// Freitag:
	$sql = "
		UPDATE 
		  `kalender`
		SET
		  `arbh_n` = '5',
		  `arbh_2` = '11.25',
		  `arbh_3` = '15.5'
		WHERE
			  BINARY `arbeitstag` = BINARY 'a'
		  AND `datum` >= '2005-08-01'
	";
	mysql_query($sql, $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
	
	// Sonntag:
	$sql = "
		UPDATE 
		  `kalender`
		SET
		  `arbh_3` = '4.25'
		WHERE
			  `arbeitstag` = '-'
		  AND `wtag` = '0'
		  AND `datum` >= '2005-08-01'
  	";
	mysql_query($sql, $con) or die("<p>Böses SQL-Statement:</p><p>$sql</p><p>".mysql_error($con)."</p>");
	
	echo "&nbsp;<br>&nbsp;<br>FERTIG !";
		
?>
```
Da sind jetzt natürlich ein paar Sachen drin, die spezifisch für meinen Arbeitgeber angepasst sind (z.B. unsere Betriebsferien, die Arbeitsstunden pro Tag oder die Sache mit der 40h-Woche). Das kann man sich ja auf den eigenen Bedarf zurecht pfriemeln.

Vielleicht hilfts,
_Martin_


----------



## 27apricot (19. März 2006)

Hallo allerseits,

also vielen Dank erstmal für die Antworten. Hatte ein paar Tage keine Zeit, mich um dieses Projekt zu kümmern.

Danke besonders auch an ManicMarble für das große Script. Das ist für mein derzeitiges Projekt etwas überdimensioniert, aber man weiß ja nie: vielleicht hilft es jemand anderem oder mir später mal.

Was ich jetzt gemacht habe, ist:
ich schreibe die Monate einer Spielzeit in ein Array und überprüfe für jeden einzelnen, ob etwas stattfindet. Das wiederum geht für alle Monate, in denen ein Anfangs- oder Enddatum steht als auch für Monate, die zwischen den zwei Daten eines Ereignisses liegen. (Siehe diese meine Anfrage im selben Forum)

Ciao denn:
27apricot


----------

