Autor Beitrag
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 16.08.11 23:45 
Moin!
Ich frag einfach mal, vielleicht hat ja einer doch noch die grandiose Idee... :think: ;)

Wie kann man im MySQL-Dialekt die Anzahl der Werktage (konkret: in NRW/DE) zwischen zwei gegebenen Datumsangaben berechnen? :nixweiss:

Aktueller Stand: Eine Tabelle calendar (theday DATE, workday TINYINT) anlegen, füllen, die Arbeitstage mit !=0 kennzeichnen (z.B. könnte man ja auch noch halbe Tage differenzieren wollen) und dann ein
ausblenden SQL-Anweisung
1:
SELECT COUNT(*) FROM calendar WHERE (theday BETWEEN d1 AND d2) AND (workday != 0)					
absetzen. :?

Andere Vorschläge? :lupe: :gruebel:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
DonManfred
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 148
Erhaltene Danke: 2

Windows 7
Delphi XE3 Pro + HTML5Builder
BeitragVerfasst: Mi 17.08.11 07:10 
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Moin!
Wie kann man im MySQL-Dialekt die Anzahl der Werktage (konkret: in NRW/DE) zwischen zwei gegebenen Datumsangaben berechnen? :nixweiss:
[..]
Andere Vorschläge? :lupe: :gruebel:


Ich würds mit

ausblenden SQL-Anweisung
1:
SELECT COUNT(*) FROM calendar WHERE (theday BETWEEN d1 AND d2) AND (DAYOFWEEK(theday) BETWEEN 2 AND 6)					


probieren. Obs von der Syntax so passt weiss ich nicht, hab noch keinen Kaffee gehabt :D

Siehe DayOfWeek

Ansonsten:

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Moin!
Wie kann man im MySQL-Dialekt die Anzahl der Werktage (konkret: in NRW/DE) zwischen zwei gegebenen Datumsangaben berechnen? :nixweiss:


Mmmmmhhhhh... Ein Montag ist in NRW, Sachsen, Bayern aber auch in England, den USA oder China ein Montag :D

_________________
Gruss Manfred


Zuletzt bearbeitet von DonManfred am Mi 17.08.11 07:38, insgesamt 1-mal bearbeitet
iKilledKenny
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 394
Erhaltene Danke: 8

Win XP
D5 Prof, C# Express 2005
BeitragVerfasst: Mi 17.08.11 07:38 
Die Frage ist, ob das denn in MySQL sein muss. Ich habe sowas in PHP realisiert, ich speichere ein Anfangsdatum und eine Frist (Anzahl Werktage) in der DB und rechne dann on the fly das Enddatum aus. Das ganze dann Bundesland abhängig, wegen der verschiedenen Feiertage.
DonManfred
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 148
Erhaltene Danke: 2

Windows 7
Delphi XE3 Pro + HTML5Builder
BeitragVerfasst: Mi 17.08.11 07:46 
user profile iconiKilledKenny hat folgendes geschrieben Zum zitierten Posting springen:
Die Frage ist, ob das denn in MySQL sein muss. Ich habe sowas in PHP realisiert, ich speichere ein Anfangsdatum und eine Frist (Anzahl Werktage) in der DB und rechne dann on the fly das Enddatum aus. Das ganze dann Bundesland abhängig, wegen der verschiedenen Feiertage.


Narses hat explizit nach einer Lösung in MySql gesucht. Ich gehe also davon aus das er bereits MySQL einsetzt. Und die Lösung ist ja recht einfach; siehe oben. PHP wird ihm da nicht gross weiter helfen da er ja in Delphi programmiert. Auch DA könnte man das sicherlich "berechnen" mit den DATE-routinen...

_________________
Gruss Manfred
iKilledKenny
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 394
Erhaltene Danke: 8

Win XP
D5 Prof, C# Express 2005
BeitragVerfasst: Mi 17.08.11 07:55 
user profile iconDonManfred hat folgendes geschrieben Zum zitierten Posting springen:
Narses hat explizit nach einer Lösung in MySql gesucht.


Glaub es oder glaub es nicht, aber das habe auch ich gelesen. Deine Lösung mit DayOfWeek berücksichtigt leider nicht die Feiertage, sondern eben nur Montag bis Freitag. Als Feiertage sind die festen und "beweglichen" Feiertage zu berücksichtigen, Stichwort sei hier Ostern.

Außerdem traue ich unserem Admin eine Portierung von PHP nach Delphi durchaus zu.
DonManfred
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 148
Erhaltene Danke: 2

Windows 7
Delphi XE3 Pro + HTML5Builder
BeitragVerfasst: Mi 17.08.11 08:10 
user profile iconiKilledKenny hat folgendes geschrieben Zum zitierten Posting springen:
Deine Lösung mit DayOfWeek berücksichtigt leider nicht die Feiertage, sondern eben nur Montag bis Freitag.


Das Narses eine Portierung erstellen könnte nehme ich auch an. ;)

Ansonsten hat er nach Wochentagen gefragt. Ein Montag ist auch am Rosenmontag ein Montag. Und wenn der 1. Weihnachtstag auf einen Montag fällt dann ist auch dieser ein Montag (also ein Wochentag). Ob da nun gearbeitet wird oder nicht (Feiertage) schien mir hier nicht relevant; wurde aber auch nicht explizit angesprochen :D

_________________
Gruss Manfred
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6393
Erhaltene Danke: 147

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 17.08.11 08:28 
Werktage gehen von Montag bis Samstag. Bei Arbeitstagen hängt es vom Gewerbe ab.
Im Gesetz steht meines Wissens, dass Werktage alle Tage sind, die nicht Sonntag heißen oder gesetzliche Feiertage sind.

Somit ist der Einwand von user profile iconiKilledKenny auf die Lösung von user profile iconDonManfred durchaus berechtigt.
Eine Lösung kann also nur über ein Kennzeichen in der Tabelle oder eine Feiertagstabelle funktionieren, die die entsprechenden Tage markiert und die Berücksichtigung des Sonntags beinhaltet. Ich würde die Lösung mit der Feiertagstabelle bevorzugen.
In Oracle würde ich es so lösen:
ausblenden SQL-Anweisung
1:
2:
3:
SELECT COUNT(*) FROM calendar 
WHERE (theday BETWEEN d1 AND d2) AND (DAYOFWEEK(theday) BETWEEN 2 AND 7)
AND not exists (select feiertag from feiertagstabelle where theday = feiertag)


Geht sicher auch mit einem not .. in, aber das ist Geschmackssache.
In mySQL wird es sicher ein ähnliches Konstrukt geben.

Für diesen Beitrag haben gedankt: elundril
DonManfred
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 148
Erhaltene Danke: 2

Windows 7
Delphi XE3 Pro + HTML5Builder
BeitragVerfasst: Mi 17.08.11 08:52 
user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Werktage gehen von Montag bis Samstag.


Kommt auf die "Anwendung" an. Im Mietrecht z.b. ist ein Werktag Mo-Fr.

Ansonsten muss ich dir - nach Googlen - Recht geben und meine Lösung von

ausblenden SQL-Anweisung
1:
SELECT COUNT(*) FROM calendar WHERE (theday BETWEEN d1 AND d2) AND (DAYOFWEEK(theday) BETWEEN 2 AND 6)					


auf

ausblenden SQL-Anweisung
1:
SELECT COUNT(*) FROM calendar WHERE (theday BETWEEN d1 AND d2) AND (DAYOFWEEK(theday) BETWEEN 2 AND 7)					


korrigieren. ;-)

_________________
Gruss Manfred
iKilledKenny
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 394
Erhaltene Danke: 8

Win XP
D5 Prof, C# Express 2005
BeitragVerfasst: Mi 17.08.11 09:06 
Das ein Wochen-Werktag (Mo-Sa) auch ein Feiertag sein kann, scheint immer noch nicht angekommen zu sein... 8)
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6393
Erhaltene Danke: 147

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 17.08.11 09:07 
Das Mietrecht macht da tatsächlich eine Ausnahme :wink:
Trotzdem gehören gesetzliche Feiertage nicht zu den Werktagen und müssen ebenfalls berücksichtigt werden.
DonManfred
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 148
Erhaltene Danke: 2

Windows 7
Delphi XE3 Pro + HTML5Builder
BeitragVerfasst: Mi 17.08.11 09:11 
user profile iconiKilledKenny hat folgendes geschrieben Zum zitierten Posting springen:
Das ein Wochen-Werktag (Mo-Sa) auch ein Feiertag sein kann, scheint immer noch nicht angekommen zu sein... 8)


Doch, ist es...

user profile iconDonManfred hat folgendes geschrieben Zum zitierten Posting springen:
Ob da nun gearbeitet wird oder nicht (Feiertage) schien mir hier nicht relevant; wurde aber auch nicht explizit angesprochen :D

_________________
Gruss Manfred
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6393
Erhaltene Danke: 147

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 17.08.11 09:29 
Jetzt werde ich mal kleinkariert. :twisted:
Die Frage bezog sich auf Werktage und nicht auf Arbeitstage. Somit muss die Berücksichtigung von gesetzlichen Feiertagen per Definition von "Werktag" enthalten sein. :wink:

Allerdings heißt das Datenbankfeld "workday", was wiederum mit Arbeitstag übersetzt werden müsste.

Man user profile iconNarses! Kannst du deine Fragen nicht besser formulieren? :motz: :mahn:
Narses Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mi 17.08.11 09:33 
Moin!

Also erstmal vielen Dank euch allen! :beer:

Ich präzisiere:
  • Ich meine mit Werktage => Arbeitstage (MO-FR ohne Feiertage in NRW/DE). :idea:
  • Ja, es muss reines MySQL sein, PHP bringt mich hier nicht weiter. :nixweiss:
  • Es scheint tatsächlich auf einen Tabellen-Lookup hinauszulaufen (wegen der beweglichen Feiertage), oder? :?
cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6393
Erhaltene Danke: 147

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 17.08.11 09:40 
Sieht so aus. Alternativ müsstest du bei jedem Eintrag in deiner Tabelle ein entsprechendes Kennzeichen setzen. Damit verlagerst du die Arbeit aber nur, weil du dann ja beim Erfassen des Tages in der Feiertagstabelle nachsehen musst.

Es ist dann wohl eher die Frage, wo es aus Performance-Gründen besser eingebaut wird.
Singlepin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36
Erhaltene Danke: 4

WinXP
Delphi6 MySQL
BeitragVerfasst: Mi 17.08.11 13:11 
Der eleganteste Weg ist wohl nur mit Stored Procedures zu realisieren.
Narses Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mi 17.08.11 17:11 
Moin!

user profile iconSinglepin hat folgendes geschrieben Zum zitierten Posting springen:
Der eleganteste Weg ist wohl nur mit Stored Procedures zu realisieren.
Na dann, ich bitte um Erleuchtung (=Codebeispiel für den Anwendungsfall, wohlgemerkt: bewegliche Feiertage!). :idea: ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Singlepin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36
Erhaltene Danke: 4

WinXP
Delphi6 MySQL
BeitragVerfasst: Mi 17.08.11 18:43 
Auf die Schnelle.

Die Prozedure könnte so aussehen:
ausblenden volle Höhe SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `wochentage`(
IN
von Date,
bis Date
)
BEGIN
DECLARE i INTEGER;
DECLARE Anzahl INTEGER;
DECLARE Datum DATE;
SET I=0;
SET Anzahl=0;
SET Datum=von;
WHILE(i<=DATEDIFF(bis,von))DO
 BEGIN
 if DAYOFWEEK(Datum) BETWEEN 2 AND 6 THEN
  BEGIN 
  -- hier müßen wir noch auf Feiertage testen.
  SET Anzahl=Anzahl+1;
  END;
 END IF;
 SET Datum=DATE(ADDDATE(Datum, INTERVAL 1 DAY));
 SET i=i+1;
 END;
 END WHILE;
SELECT Anzahl;
END
Die Pocedure wird so aufgerufen:
ausblenden SQL-Anweisung
1:
CALL DB.wochentage("2011-01-14","2011-02-07");					
Da wo jetzt der Kommentar steht müssen wir nur noch auf Feiertage prüfen.
Bei Festen ist das ja kein Problem.
Bei Beweglichen berechnen wir Ostersonntag und dann den Rest.

Mach ich Morgen fertig.

Ich hoffe es ist das was gebraucht wird.

Moderiert von user profile iconNarses: SQL-Tags hinzugefügt
Singlepin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36
Erhaltene Danke: 4

WinXP
Delphi6 MySQL
BeitragVerfasst: Do 18.08.11 08:19 
So nun wollen wir mal die Sache zum Abschluß bringen.

Wir brauchen Ostersonntag, dazu hatte ich mal folgende Formel gefunden:

J = 2011
d = REST(REST(J;19)*19+24;30)
dkorr = d-(d>27)
WoTag = REST(GANZZAHL(J*5/4+dkorr+1);7)
Ostern = DATUM(J;3;28)+dkorr-WoTag

mit MySQL sieht das so aus:
ausblenden SQL-Anweisung
1:
2:
SET @B40=2011;
SELECT DATE(ADDDATE(CONCAT(@B40,"-03-28"), INTERVAL ((((@B40%19)*19+24)%30)-((((@B40%19)*19+24)%30)>27)-((FLOOR(@B40*5/4+((((@B40%19)*19+24)%30)-((((@B40%19)*19+24)%30)>27))+1))%7)) DAY));

Die übrigen beweglichen Feiertage berechnen sich so:

Weiber Fastnacht Ostern - 52
Rosenmontag Ostern - 48
Aschermittwoch Ostern - 46
Karfreitag Ostern - 2
Ostersonntag Ostern
Ostermontag Ostern + 1
Christi Himmelfahrt Ostern + 39
Pfingstsonntag Ostern + 49
Pfingstmontag Ostern + 50
Fronleichnam Ostern + 60

Dann müssen wir meine Precedure nur noch erweiter mit:
ausblenden SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
DECLARE Jahr INTEGER;
DECLARE Ostern DATE;
...
  SET Jahr=YEAR(Datum);
  SET Ostern=DATE(ADDDATE(CONCAT(Jahr,"-03-28"), INTERVAL ((((Jahr%19)*19+24)%30)-((((Jahr%19)*19+24)%30)>27)-((FLOOR(Jahr*5/4+((((Jahr%19)*19+24)%30)-((((Jahr%19)*19+24)%30)>27))+1))%7)) DAY));
--  if Datum=DATE(ADDDATE(Ostern, INTERVAL -52 DAY)) then SET Anzahl=Anzahl-1;END IF; -- Weiberfastnacht
--  if Datum=DATE(ADDDATE(Ostern, INTERVAL -48 DAY)) then SET Anzahl=Anzahl-1;END IF; -- Rosenmontag
--  if Datum=DATE(ADDDATE(Ostern, INTERVAL -46 DAY)) then SET Anzahl=Anzahl-1;END IF; -- Aschermittwoch
    if Datum=DATE(ADDDATE(Ostern, INTERVAL -2 DAY)) then SET Anzahl=Anzahl-1;END IF;  -- Karfreitag
    if Datum=DATE(ADDDATE(Ostern, INTERVAL 1 DAY)) then SET Anzahl=Anzahl-1;END IF;   -- Ostermontag
    if Datum=DATE(ADDDATE(Ostern, INTERVAL 39 DAY)) then SET Anzahl=Anzahl-1;END IF;  -- Christi Himmelfahrt
--  if Datum=DATE(ADDDATE(Ostern, INTERVAL 49 DAY)) then SET Anzahl=Anzahl-1;END IF;  -- Pfingstsonntag
    if Datum=DATE(ADDDATE(Ostern, INTERVAL 50 DAY)) then SET Anzahl=Anzahl-1;END IF;  -- Pfingstmontag
    if Datum=DATE(ADDDATE(Ostern, INTERVAL 60 DAY)) then SET Anzahl=Anzahl-1;END IF;  -- Fronleichnam
-- hier müßen wir noch auf festen Feiertage testen.
    if Datum=CONCAT(Jahr,'-01-01'then SET Anzahl=Anzahl-1;END IF; -- Neujahr
--  if Datum=CONCAT(Jahr,'-01-06'then SET Anzahl=Anzahl-1;END IF; -- Heilige Drei Könige
    if Datum=CONCAT(Jahr,'-05-01'then SET Anzahl=Anzahl-1;END IF; -- Maifeiertag
--  if Datum=CONCAT(Jahr,'-08-15'then SET Anzahl=Anzahl-1;END IF; -- MariÄ Himmelfahrt
    if Datum=CONCAT(Jahr,'-10-03'then SET Anzahl=Anzahl-1;END IF; -- Tag der deutschen Einheit
--  if Datum=CONCAT(Jahr,'-10-31'then SET Anzahl=Anzahl-1;END IF; -- Reformationstag
    if Datum=CONCAT(Jahr,'-11-01'then SET Anzahl=Anzahl-1;END IF; -- Allerheiligen
-- Buß-und Bettag letzter Mitwoch vor dem 23.November 
    if Datum=CONCAT(Jahr,'-12-25'then SET Anzahl=Anzahl-1;END IF; -- 1. Weihnachtstag
    if Datum=CONCAT(Jahr,'-12-26'then SET Anzahl=Anzahl-1;END IF; -- 2. Weinachttstag
...

Die ständige Berechnung von Ostersonntag kann man noch einschränken, in dem man das nur beim Jahreswechsel macht. usw.


Zuletzt bearbeitet von Singlepin am Fr 19.08.11 17:04, insgesamt 1-mal bearbeitet
Narses Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 18.08.11 11:21 
Moin!

Interessanter Ansatz, vielen Dank! :zustimm: Muss ich mir nochmal genauer ansehen. :lupe:

Allerdings habe ich da noch ein praktisches Problem. Ich zeige mal meinen Code, wo diese Differenz ermittelt wird (aktuell noch mit Kalendertagen):
ausblenden SQL-Anweisung
1:
SELECT irgendwas... , DATEDIFF(CURDATE(), statechg) AS daycnt FROM irgendwo ...					
Es geht also darum das "daycnt" auf Werktage umzustellen. :idea:

Wenn ich mich recht erinnere, dann liefert eine SP aber ein Result-Set (genau genommen sogar zwei, hinterher nämlich noch ein leeres als Ende-Marker). Wie soll ich das mit dem Select-Statement oben zusammen bringen? :? :nixweiss: der aufmerksame Leser merkt schon, da hat einer noch nicht ganz so viel mit SPs gemacht... *schäm*

Generelle Überlegung: wie immer bei diesen Problemen ist das Produkt aus Rechenzeit und Speicherplatzbedarf konstant, man ändert den einen Faktor nur auf Kosten des anderen. Damit stellt sich die Frage: wann ist was effizienter? :gruebel:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Singlepin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36
Erhaltene Danke: 4

WinXP
Delphi6 MySQL
BeitragVerfasst: Do 18.08.11 12:51 
Hallo Narses,

eine SP muß nicht zwingend ein Result-Set haben.
In meiner SP die Select-Anweisung durch SET @meineVariable ersetzten.
Die SP aufrufen und anschließend SELECT @meineVariable; könnte vieleicht ein Weg sein.

zu deiner generellen Überlegung.
Ich ziehe eine Lösung die durch eine komplexere Berechnung mein Problem löst vor.
- kein weiterer Pflegeaufwand bis der Gesetzgeber was ändert.
- keine Eingabefehler
- die Funktionsgrenzen sind theoretisch unendlich, alternativ braut man unendlich Speicherplatz
- schnell änderbar, falls man später noch weiß was man damals gemacht hat.
Natürlich abgesehen von den Fällen wo die Rechenzeit nicht mehr akzeptabel ist.